xref: /openbmc/linux/net/dccp/proto.c (revision 7690af3fff7633e40b1b9950eb8489129251d074)
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,
1507c657876SArnaldo Carvalho de Melo 		    char *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,
1617c657876SArnaldo Carvalho de Melo 		    char *optval, int *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 EXPORT_SYMBOL(dccp_sendmsg);
2187c657876SArnaldo Carvalho de Melo 
2197c657876SArnaldo Carvalho de Melo int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
2207c657876SArnaldo Carvalho de Melo 		 size_t len, int nonblock, int flags, int *addr_len)
2217c657876SArnaldo Carvalho de Melo {
2227c657876SArnaldo Carvalho de Melo 	const struct dccp_hdr *dh;
2237c657876SArnaldo Carvalho de Melo 	int copied = 0;
2247c657876SArnaldo Carvalho de Melo 	unsigned long used;
2257c657876SArnaldo Carvalho de Melo 	int err;
2267c657876SArnaldo Carvalho de Melo 	int target;		/* Read at least this many bytes */
2277c657876SArnaldo Carvalho de Melo 	long timeo;
2287c657876SArnaldo Carvalho de Melo 
2297c657876SArnaldo Carvalho de Melo 	lock_sock(sk);
2307c657876SArnaldo Carvalho de Melo 
2317c657876SArnaldo Carvalho de Melo 	err = -ENOTCONN;
2327c657876SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_LISTEN)
2337c657876SArnaldo Carvalho de Melo 		goto out;
2347c657876SArnaldo Carvalho de Melo 
2357c657876SArnaldo Carvalho de Melo 	timeo = sock_rcvtimeo(sk, nonblock);
2367c657876SArnaldo Carvalho de Melo 
2377c657876SArnaldo Carvalho de Melo 	/* Urgent data needs to be handled specially. */
2387c657876SArnaldo Carvalho de Melo 	if (flags & MSG_OOB)
2397c657876SArnaldo Carvalho de Melo 		goto recv_urg;
2407c657876SArnaldo Carvalho de Melo 
2417c657876SArnaldo Carvalho de Melo 	/* FIXME */
2427c657876SArnaldo Carvalho de Melo #if 0
2437c657876SArnaldo Carvalho de Melo 	seq = &tp->copied_seq;
2447c657876SArnaldo Carvalho de Melo 	if (flags & MSG_PEEK) {
2457c657876SArnaldo Carvalho de Melo 		peek_seq = tp->copied_seq;
2467c657876SArnaldo Carvalho de Melo 		seq = &peek_seq;
2477c657876SArnaldo Carvalho de Melo 	}
2487c657876SArnaldo Carvalho de Melo #endif
2497c657876SArnaldo Carvalho de Melo 
2507c657876SArnaldo Carvalho de Melo 	target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
2517c657876SArnaldo Carvalho de Melo 
2527c657876SArnaldo Carvalho de Melo 	do {
2537c657876SArnaldo Carvalho de Melo 		struct sk_buff *skb;
2547c657876SArnaldo Carvalho de Melo 		u32 offset;
2557c657876SArnaldo Carvalho de Melo 
2567c657876SArnaldo Carvalho de Melo 	/* FIXME */
2577c657876SArnaldo Carvalho de Melo #if 0
258*7690af3fSArnaldo Carvalho de Melo 		/*
259*7690af3fSArnaldo Carvalho de Melo 		 * Are we at urgent data? Stop if we have read anything or
260*7690af3fSArnaldo Carvalho de Melo 		 * have SIGURG pending.
261*7690af3fSArnaldo Carvalho de Melo 		 */
2627c657876SArnaldo Carvalho de Melo 		if (tp->urg_data && tp->urg_seq == *seq) {
2637c657876SArnaldo Carvalho de Melo 			if (copied)
2647c657876SArnaldo Carvalho de Melo 				break;
2657c657876SArnaldo Carvalho de Melo 			if (signal_pending(current)) {
266*7690af3fSArnaldo Carvalho de Melo 				copied = timeo ? sock_intr_errno(timeo) :
267*7690af3fSArnaldo Carvalho de Melo 						 -EAGAIN;
2687c657876SArnaldo Carvalho de Melo 				break;
2697c657876SArnaldo Carvalho de Melo 			}
2707c657876SArnaldo Carvalho de Melo 		}
2717c657876SArnaldo Carvalho de Melo #endif
2727c657876SArnaldo Carvalho de Melo 
2737c657876SArnaldo Carvalho de Melo 		/* Next get a buffer. */
2747c657876SArnaldo Carvalho de Melo 
2757c657876SArnaldo Carvalho de Melo 		skb = skb_peek(&sk->sk_receive_queue);
2767c657876SArnaldo Carvalho de Melo 		do {
2777c657876SArnaldo Carvalho de Melo 			if (!skb)
2787c657876SArnaldo Carvalho de Melo 				break;
2797c657876SArnaldo Carvalho de Melo 
2807c657876SArnaldo Carvalho de Melo 			offset = 0;
2817c657876SArnaldo Carvalho de Melo 			dh = dccp_hdr(skb);
2827c657876SArnaldo Carvalho de Melo 
2837c657876SArnaldo Carvalho de Melo 			if (dh->dccph_type == DCCP_PKT_DATA ||
2847c657876SArnaldo Carvalho de Melo 			    dh->dccph_type == DCCP_PKT_DATAACK)
2857c657876SArnaldo Carvalho de Melo 				goto found_ok_skb;
2867c657876SArnaldo Carvalho de Melo 
2877c657876SArnaldo Carvalho de Melo 			if (dh->dccph_type == DCCP_PKT_RESET ||
2887c657876SArnaldo Carvalho de Melo 			    dh->dccph_type == DCCP_PKT_CLOSE) {
2897c657876SArnaldo Carvalho de Melo 				dccp_pr_debug("found fin ok!\n");
2907c657876SArnaldo Carvalho de Melo 				goto found_fin_ok;
2917c657876SArnaldo Carvalho de Melo 			}
292*7690af3fSArnaldo Carvalho de Melo 			dccp_pr_debug("packet_type=%s\n",
293*7690af3fSArnaldo Carvalho de Melo 				      dccp_packet_name(dh->dccph_type));
2947c657876SArnaldo Carvalho de Melo 			BUG_TRAP(flags & MSG_PEEK);
2957c657876SArnaldo Carvalho de Melo 			skb = skb->next;
2967c657876SArnaldo Carvalho de Melo 		} while (skb != (struct sk_buff *)&sk->sk_receive_queue);
2977c657876SArnaldo Carvalho de Melo 
2987c657876SArnaldo Carvalho de Melo 		/* Well, if we have backlog, try to process it now yet. */
2997c657876SArnaldo Carvalho de Melo 		if (copied >= target && !sk->sk_backlog.tail)
3007c657876SArnaldo Carvalho de Melo 			break;
3017c657876SArnaldo Carvalho de Melo 
3027c657876SArnaldo Carvalho de Melo 		if (copied) {
3037c657876SArnaldo Carvalho de Melo 			if (sk->sk_err ||
3047c657876SArnaldo Carvalho de Melo 			    sk->sk_state == DCCP_CLOSED ||
3057c657876SArnaldo Carvalho de Melo 			    (sk->sk_shutdown & RCV_SHUTDOWN) ||
3067c657876SArnaldo Carvalho de Melo 			    !timeo ||
3077c657876SArnaldo Carvalho de Melo 			    signal_pending(current) ||
3087c657876SArnaldo Carvalho de Melo 			    (flags & MSG_PEEK))
3097c657876SArnaldo Carvalho de Melo 				break;
3107c657876SArnaldo Carvalho de Melo 		} else {
3117c657876SArnaldo Carvalho de Melo 			if (sock_flag(sk, SOCK_DONE))
3127c657876SArnaldo Carvalho de Melo 				break;
3137c657876SArnaldo Carvalho de Melo 
3147c657876SArnaldo Carvalho de Melo 			if (sk->sk_err) {
3157c657876SArnaldo Carvalho de Melo 				copied = sock_error(sk);
3167c657876SArnaldo Carvalho de Melo 				break;
3177c657876SArnaldo Carvalho de Melo 			}
3187c657876SArnaldo Carvalho de Melo 
3197c657876SArnaldo Carvalho de Melo 			if (sk->sk_shutdown & RCV_SHUTDOWN)
3207c657876SArnaldo Carvalho de Melo 				break;
3217c657876SArnaldo Carvalho de Melo 
3227c657876SArnaldo Carvalho de Melo 			if (sk->sk_state == DCCP_CLOSED) {
3237c657876SArnaldo Carvalho de Melo 				if (!sock_flag(sk, SOCK_DONE)) {
3247c657876SArnaldo Carvalho de Melo 					/* This occurs when user tries to read
3257c657876SArnaldo Carvalho de Melo 					 * from never connected socket.
3267c657876SArnaldo Carvalho de Melo 					 */
3277c657876SArnaldo Carvalho de Melo 					copied = -ENOTCONN;
3287c657876SArnaldo Carvalho de Melo 					break;
3297c657876SArnaldo Carvalho de Melo 				}
3307c657876SArnaldo Carvalho de Melo 				break;
3317c657876SArnaldo Carvalho de Melo 			}
3327c657876SArnaldo Carvalho de Melo 
3337c657876SArnaldo Carvalho de Melo 			if (!timeo) {
3347c657876SArnaldo Carvalho de Melo 				copied = -EAGAIN;
3357c657876SArnaldo Carvalho de Melo 				break;
3367c657876SArnaldo Carvalho de Melo 			}
3377c657876SArnaldo Carvalho de Melo 
3387c657876SArnaldo Carvalho de Melo 			if (signal_pending(current)) {
3397c657876SArnaldo Carvalho de Melo 				copied = sock_intr_errno(timeo);
3407c657876SArnaldo Carvalho de Melo 				break;
3417c657876SArnaldo Carvalho de Melo 			}
3427c657876SArnaldo Carvalho de Melo 		}
3437c657876SArnaldo Carvalho de Melo 
3447c657876SArnaldo Carvalho de Melo 		/* FIXME: cleanup_rbuf(sk, copied); */
3457c657876SArnaldo Carvalho de Melo 
3467c657876SArnaldo Carvalho de Melo 		if (copied >= target) {
3477c657876SArnaldo Carvalho de Melo 			/* Do not sleep, just process backlog. */
3487c657876SArnaldo Carvalho de Melo 			release_sock(sk);
3497c657876SArnaldo Carvalho de Melo 			lock_sock(sk);
3507c657876SArnaldo Carvalho de Melo 		} else
3517c657876SArnaldo Carvalho de Melo 			sk_wait_data(sk, &timeo);
3527c657876SArnaldo Carvalho de Melo 
3537c657876SArnaldo Carvalho de Melo 		continue;
3547c657876SArnaldo Carvalho de Melo 
3557c657876SArnaldo Carvalho de Melo 	found_ok_skb:
3567c657876SArnaldo Carvalho de Melo 		/* Ok so how much can we use? */
3577c657876SArnaldo Carvalho de Melo 		used = skb->len - offset;
3587c657876SArnaldo Carvalho de Melo 		if (len < used)
3597c657876SArnaldo Carvalho de Melo 			used = len;
3607c657876SArnaldo Carvalho de Melo 
3617c657876SArnaldo Carvalho de Melo 		if (!(flags & MSG_TRUNC)) {
3627c657876SArnaldo Carvalho de Melo 			err = skb_copy_datagram_iovec(skb, offset,
3637c657876SArnaldo Carvalho de Melo 						      msg->msg_iov, used);
3647c657876SArnaldo Carvalho de Melo 			if (err) {
3657c657876SArnaldo Carvalho de Melo 				/* Exception. Bailout! */
3667c657876SArnaldo Carvalho de Melo 				if (!copied)
3677c657876SArnaldo Carvalho de Melo 					copied = -EFAULT;
3687c657876SArnaldo Carvalho de Melo 				break;
3697c657876SArnaldo Carvalho de Melo 			}
3707c657876SArnaldo Carvalho de Melo 		}
3717c657876SArnaldo Carvalho de Melo 
3727c657876SArnaldo Carvalho de Melo 		copied += used;
3737c657876SArnaldo Carvalho de Melo 		len -= used;
3747c657876SArnaldo Carvalho de Melo 
3757c657876SArnaldo Carvalho de Melo 		/* FIXME: tcp_rcv_space_adjust(sk); */
3767c657876SArnaldo Carvalho de Melo 
3777c657876SArnaldo Carvalho de Melo //skip_copy:
3787c657876SArnaldo Carvalho de Melo 		if (used + offset < skb->len)
3797c657876SArnaldo Carvalho de Melo 			continue;
3807c657876SArnaldo Carvalho de Melo 
3817c657876SArnaldo Carvalho de Melo 		if (!(flags & MSG_PEEK))
3827c657876SArnaldo Carvalho de Melo 			sk_eat_skb(sk, skb);
3837c657876SArnaldo Carvalho de Melo 		continue;
3847c657876SArnaldo Carvalho de Melo 	found_fin_ok:
3857c657876SArnaldo Carvalho de Melo 		if (!(flags & MSG_PEEK))
3867c657876SArnaldo Carvalho de Melo 			sk_eat_skb(sk, skb);
3877c657876SArnaldo Carvalho de Melo 		break;
3887c657876SArnaldo Carvalho de Melo 
3897c657876SArnaldo Carvalho de Melo 	} while (len > 0);
3907c657876SArnaldo Carvalho de Melo 
3917c657876SArnaldo Carvalho de Melo 	/* According to UNIX98, msg_name/msg_namelen are ignored
3927c657876SArnaldo Carvalho de Melo 	 * on connected socket. I was just happy when found this 8) --ANK
3937c657876SArnaldo Carvalho de Melo 	 */
3947c657876SArnaldo Carvalho de Melo 
3957c657876SArnaldo Carvalho de Melo 	/* Clean up data we have read: This will do ACK frames. */
3967c657876SArnaldo Carvalho de Melo 	/* FIXME: cleanup_rbuf(sk, copied); */
3977c657876SArnaldo Carvalho de Melo 
3987c657876SArnaldo Carvalho de Melo 	release_sock(sk);
3997c657876SArnaldo Carvalho de Melo 	return copied;
4007c657876SArnaldo Carvalho de Melo 
4017c657876SArnaldo Carvalho de Melo out:
4027c657876SArnaldo Carvalho de Melo 	release_sock(sk);
4037c657876SArnaldo Carvalho de Melo 	return err;
4047c657876SArnaldo Carvalho de Melo 
4057c657876SArnaldo Carvalho de Melo recv_urg:
4067c657876SArnaldo Carvalho de Melo 	/* FIXME: err = tcp_recv_urg(sk, timeo, msg, len, flags, addr_len); */
4077c657876SArnaldo Carvalho de Melo 	goto out;
4087c657876SArnaldo Carvalho de Melo }
4097c657876SArnaldo Carvalho de Melo 
4107c657876SArnaldo Carvalho de Melo static int inet_dccp_listen(struct socket *sock, int backlog)
4117c657876SArnaldo Carvalho de Melo {
4127c657876SArnaldo Carvalho de Melo 	struct sock *sk = sock->sk;
4137c657876SArnaldo Carvalho de Melo 	unsigned char old_state;
4147c657876SArnaldo Carvalho de Melo 	int err;
4157c657876SArnaldo Carvalho de Melo 
4167c657876SArnaldo Carvalho de Melo 	lock_sock(sk);
4177c657876SArnaldo Carvalho de Melo 
4187c657876SArnaldo Carvalho de Melo 	err = -EINVAL;
4197c657876SArnaldo Carvalho de Melo 	if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP)
4207c657876SArnaldo Carvalho de Melo 		goto out;
4217c657876SArnaldo Carvalho de Melo 
4227c657876SArnaldo Carvalho de Melo 	old_state = sk->sk_state;
4237c657876SArnaldo Carvalho de Melo 	if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN)))
4247c657876SArnaldo Carvalho de Melo 		goto out;
4257c657876SArnaldo Carvalho de Melo 
4267c657876SArnaldo Carvalho de Melo 	/* Really, if the socket is already in listen state
4277c657876SArnaldo Carvalho de Melo 	 * we can only allow the backlog to be adjusted.
4287c657876SArnaldo Carvalho de Melo 	 */
4297c657876SArnaldo Carvalho de Melo 	if (old_state != DCCP_LISTEN) {
4307c657876SArnaldo Carvalho de Melo 		/*
4317c657876SArnaldo Carvalho de Melo 		 * FIXME: here it probably should be sk->sk_prot->listen_start
4327c657876SArnaldo Carvalho de Melo 		 * see tcp_listen_start
4337c657876SArnaldo Carvalho de Melo 		 */
4347c657876SArnaldo Carvalho de Melo 		err = dccp_listen_start(sk);
4357c657876SArnaldo Carvalho de Melo 		if (err)
4367c657876SArnaldo Carvalho de Melo 			goto out;
4377c657876SArnaldo Carvalho de Melo 	}
4387c657876SArnaldo Carvalho de Melo 	sk->sk_max_ack_backlog = backlog;
4397c657876SArnaldo Carvalho de Melo 	err = 0;
4407c657876SArnaldo Carvalho de Melo 
4417c657876SArnaldo Carvalho de Melo out:
4427c657876SArnaldo Carvalho de Melo 	release_sock(sk);
4437c657876SArnaldo Carvalho de Melo 	return err;
4447c657876SArnaldo Carvalho de Melo }
4457c657876SArnaldo Carvalho de Melo 
4467c657876SArnaldo Carvalho de Melo static const unsigned char dccp_new_state[] = {
4477c657876SArnaldo Carvalho de Melo 	/* current state:   new state:      action:	*/
4487c657876SArnaldo Carvalho de Melo 	[0]		  = DCCP_CLOSED,
4497c657876SArnaldo Carvalho de Melo 	[DCCP_OPEN] 	  = DCCP_CLOSING | DCCP_ACTION_FIN,
4507c657876SArnaldo Carvalho de Melo 	[DCCP_REQUESTING] = DCCP_CLOSED,
4517c657876SArnaldo Carvalho de Melo 	[DCCP_PARTOPEN]	  = DCCP_CLOSING | DCCP_ACTION_FIN,
4527c657876SArnaldo Carvalho de Melo 	[DCCP_LISTEN]	  = DCCP_CLOSED,
4537c657876SArnaldo Carvalho de Melo 	[DCCP_RESPOND]	  = DCCP_CLOSED,
4547c657876SArnaldo Carvalho de Melo 	[DCCP_CLOSING]	  = DCCP_CLOSED,
4557c657876SArnaldo Carvalho de Melo 	[DCCP_TIME_WAIT]  = DCCP_CLOSED,
4567c657876SArnaldo Carvalho de Melo 	[DCCP_CLOSED]	  = DCCP_CLOSED,
4577c657876SArnaldo Carvalho de Melo };
4587c657876SArnaldo Carvalho de Melo 
4597c657876SArnaldo Carvalho de Melo static int dccp_close_state(struct sock *sk)
4607c657876SArnaldo Carvalho de Melo {
4617c657876SArnaldo Carvalho de Melo 	const int next = dccp_new_state[sk->sk_state];
4627c657876SArnaldo Carvalho de Melo 	const int ns = next & DCCP_STATE_MASK;
4637c657876SArnaldo Carvalho de Melo 
4647c657876SArnaldo Carvalho de Melo 	if (ns != sk->sk_state)
4657c657876SArnaldo Carvalho de Melo 		dccp_set_state(sk, ns);
4667c657876SArnaldo Carvalho de Melo 
4677c657876SArnaldo Carvalho de Melo 	return next & DCCP_ACTION_FIN;
4687c657876SArnaldo Carvalho de Melo }
4697c657876SArnaldo Carvalho de Melo 
4707c657876SArnaldo Carvalho de Melo void dccp_close(struct sock *sk, long timeout)
4717c657876SArnaldo Carvalho de Melo {
4727c657876SArnaldo Carvalho de Melo 	struct sk_buff *skb;
4737c657876SArnaldo Carvalho de Melo 
4747c657876SArnaldo Carvalho de Melo 	lock_sock(sk);
4757c657876SArnaldo Carvalho de Melo 
4767c657876SArnaldo Carvalho de Melo 	sk->sk_shutdown = SHUTDOWN_MASK;
4777c657876SArnaldo Carvalho de Melo 
4787c657876SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_LISTEN) {
4797c657876SArnaldo Carvalho de Melo 		dccp_set_state(sk, DCCP_CLOSED);
4807c657876SArnaldo Carvalho de Melo 
4817c657876SArnaldo Carvalho de Melo 		/* Special case. */
4827c657876SArnaldo Carvalho de Melo 		inet_csk_listen_stop(sk);
4837c657876SArnaldo Carvalho de Melo 
4847c657876SArnaldo Carvalho de Melo 		goto adjudge_to_death;
4857c657876SArnaldo Carvalho de Melo 	}
4867c657876SArnaldo Carvalho de Melo 
4877c657876SArnaldo Carvalho de Melo 	/*
4887c657876SArnaldo Carvalho de Melo 	 * We need to flush the recv. buffs.  We do this only on the
4897c657876SArnaldo Carvalho de Melo 	 * descriptor close, not protocol-sourced closes, because the
4907c657876SArnaldo Carvalho de Melo 	  *reader process may not have drained the data yet!
4917c657876SArnaldo Carvalho de Melo 	 */
4927c657876SArnaldo Carvalho de Melo 	/* FIXME: check for unread data */
4937c657876SArnaldo Carvalho de Melo 	while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
4947c657876SArnaldo Carvalho de Melo 		__kfree_skb(skb);
4957c657876SArnaldo Carvalho de Melo 	}
4967c657876SArnaldo Carvalho de Melo 
4977c657876SArnaldo Carvalho de Melo 	if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
4987c657876SArnaldo Carvalho de Melo 		/* Check zero linger _after_ checking for unread data. */
4997c657876SArnaldo Carvalho de Melo 		sk->sk_prot->disconnect(sk, 0);
5007c657876SArnaldo Carvalho de Melo 	} else if (dccp_close_state(sk)) {
5017c657876SArnaldo Carvalho de Melo 		dccp_send_close(sk);
5027c657876SArnaldo Carvalho de Melo 	}
5037c657876SArnaldo Carvalho de Melo 
5047c657876SArnaldo Carvalho de Melo 	sk_stream_wait_close(sk, timeout);
5057c657876SArnaldo Carvalho de Melo 
5067c657876SArnaldo Carvalho de Melo adjudge_to_death:
5077c657876SArnaldo Carvalho de Melo 	release_sock(sk);
5087c657876SArnaldo Carvalho de Melo 	/*
5097c657876SArnaldo Carvalho de Melo 	 * Now socket is owned by kernel and we acquire BH lock
5107c657876SArnaldo Carvalho de Melo 	 * to finish close. No need to check for user refs.
5117c657876SArnaldo Carvalho de Melo 	 */
5127c657876SArnaldo Carvalho de Melo 	local_bh_disable();
5137c657876SArnaldo Carvalho de Melo 	bh_lock_sock(sk);
5147c657876SArnaldo Carvalho de Melo 	BUG_TRAP(!sock_owned_by_user(sk));
5157c657876SArnaldo Carvalho de Melo 
5167c657876SArnaldo Carvalho de Melo 	sock_hold(sk);
5177c657876SArnaldo Carvalho de Melo 	sock_orphan(sk);
5187c657876SArnaldo Carvalho de Melo 
5197c657876SArnaldo Carvalho de Melo 	if (sk->sk_state != DCCP_CLOSED)
5207c657876SArnaldo Carvalho de Melo 		dccp_set_state(sk, DCCP_CLOSED);
5217c657876SArnaldo Carvalho de Melo 
5227c657876SArnaldo Carvalho de Melo 	atomic_inc(&dccp_orphan_count);
5237c657876SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_CLOSED)
5247c657876SArnaldo Carvalho de Melo 		inet_csk_destroy_sock(sk);
5257c657876SArnaldo Carvalho de Melo 
5267c657876SArnaldo Carvalho de Melo 	/* Otherwise, socket is reprieved until protocol close. */
5277c657876SArnaldo Carvalho de Melo 
5287c657876SArnaldo Carvalho de Melo 	bh_unlock_sock(sk);
5297c657876SArnaldo Carvalho de Melo 	local_bh_enable();
5307c657876SArnaldo Carvalho de Melo 	sock_put(sk);
5317c657876SArnaldo Carvalho de Melo }
5327c657876SArnaldo Carvalho de Melo 
5337c657876SArnaldo Carvalho de Melo void dccp_shutdown(struct sock *sk, int how)
5347c657876SArnaldo Carvalho de Melo {
5357c657876SArnaldo Carvalho de Melo 	dccp_pr_debug("entry\n");
5367c657876SArnaldo Carvalho de Melo }
5377c657876SArnaldo Carvalho de Melo 
5387c657876SArnaldo Carvalho de Melo struct proto_ops inet_dccp_ops = {
5397c657876SArnaldo Carvalho de Melo 	.family		= PF_INET,
5407c657876SArnaldo Carvalho de Melo 	.owner		= THIS_MODULE,
5417c657876SArnaldo Carvalho de Melo 	.release	= inet_release,
5427c657876SArnaldo Carvalho de Melo 	.bind		= inet_bind,
5437c657876SArnaldo Carvalho de Melo 	.connect	= inet_stream_connect,
5447c657876SArnaldo Carvalho de Melo 	.socketpair	= sock_no_socketpair,
5457c657876SArnaldo Carvalho de Melo 	.accept		= inet_accept,
5467c657876SArnaldo Carvalho de Melo 	.getname	= inet_getname,
5477c657876SArnaldo Carvalho de Melo 	.poll		= sock_no_poll,
5487c657876SArnaldo Carvalho de Melo 	.ioctl		= inet_ioctl,
549*7690af3fSArnaldo Carvalho de Melo 	/* FIXME: work on inet_listen to rename it to sock_common_listen */
550*7690af3fSArnaldo Carvalho de Melo 	.listen		= inet_dccp_listen,
5517c657876SArnaldo Carvalho de Melo 	.shutdown	= inet_shutdown,
5527c657876SArnaldo Carvalho de Melo 	.setsockopt	= sock_common_setsockopt,
5537c657876SArnaldo Carvalho de Melo 	.getsockopt	= sock_common_getsockopt,
5547c657876SArnaldo Carvalho de Melo 	.sendmsg	= inet_sendmsg,
5557c657876SArnaldo Carvalho de Melo 	.recvmsg	= sock_common_recvmsg,
5567c657876SArnaldo Carvalho de Melo 	.mmap		= sock_no_mmap,
5577c657876SArnaldo Carvalho de Melo 	.sendpage	= sock_no_sendpage,
5587c657876SArnaldo Carvalho de Melo };
5597c657876SArnaldo Carvalho de Melo 
5607c657876SArnaldo Carvalho de Melo extern struct net_proto_family inet_family_ops;
5617c657876SArnaldo Carvalho de Melo 
5627c657876SArnaldo Carvalho de Melo static struct inet_protosw dccp_v4_protosw = {
5637c657876SArnaldo Carvalho de Melo 	.type		= SOCK_DCCP,
5647c657876SArnaldo Carvalho de Melo 	.protocol	= IPPROTO_DCCP,
5657c657876SArnaldo Carvalho de Melo 	.prot		= &dccp_v4_prot,
5667c657876SArnaldo Carvalho de Melo 	.ops		= &inet_dccp_ops,
5677c657876SArnaldo Carvalho de Melo 	.capability	= -1,
5687c657876SArnaldo Carvalho de Melo 	.no_check	= 0,
5697c657876SArnaldo Carvalho de Melo 	.flags		= 0,
5707c657876SArnaldo Carvalho de Melo };
5717c657876SArnaldo Carvalho de Melo 
5727c657876SArnaldo Carvalho de Melo /*
5737c657876SArnaldo Carvalho de Melo  * This is the global socket data structure used for responding to
5747c657876SArnaldo Carvalho de Melo  * the Out-of-the-blue (OOTB) packets. A control sock will be created
5757c657876SArnaldo Carvalho de Melo  * for this socket at the initialization time.
5767c657876SArnaldo Carvalho de Melo  */
5777c657876SArnaldo Carvalho de Melo struct socket *dccp_ctl_socket;
5787c657876SArnaldo Carvalho de Melo 
5797c657876SArnaldo Carvalho de Melo static char dccp_ctl_socket_err_msg[] __initdata =
5807c657876SArnaldo Carvalho de Melo 	KERN_ERR "DCCP: Failed to create the control socket.\n";
5817c657876SArnaldo Carvalho de Melo 
5827c657876SArnaldo Carvalho de Melo static int __init dccp_ctl_sock_init(void)
5837c657876SArnaldo Carvalho de Melo {
5847c657876SArnaldo Carvalho de Melo 	int rc = sock_create_kern(PF_INET, SOCK_DCCP, IPPROTO_DCCP,
5857c657876SArnaldo Carvalho de Melo 				  &dccp_ctl_socket);
5867c657876SArnaldo Carvalho de Melo 	if (rc < 0)
5877c657876SArnaldo Carvalho de Melo 		printk(dccp_ctl_socket_err_msg);
5887c657876SArnaldo Carvalho de Melo 	else {
5897c657876SArnaldo Carvalho de Melo 		dccp_ctl_socket->sk->sk_allocation = GFP_ATOMIC;
5907c657876SArnaldo Carvalho de Melo 		inet_sk(dccp_ctl_socket->sk)->uc_ttl = -1;
5917c657876SArnaldo Carvalho de Melo 
5927c657876SArnaldo Carvalho de Melo 		/* Unhash it so that IP input processing does not even
5937c657876SArnaldo Carvalho de Melo 		 * see it, we do not wish this socket to see incoming
5947c657876SArnaldo Carvalho de Melo 		 * packets.
5957c657876SArnaldo Carvalho de Melo 		 */
5967c657876SArnaldo Carvalho de Melo 		dccp_ctl_socket->sk->sk_prot->unhash(dccp_ctl_socket->sk);
5977c657876SArnaldo Carvalho de Melo 	}
5987c657876SArnaldo Carvalho de Melo 
5997c657876SArnaldo Carvalho de Melo 	return rc;
6007c657876SArnaldo Carvalho de Melo }
6017c657876SArnaldo Carvalho de Melo 
6027c657876SArnaldo Carvalho de Melo static void __exit dccp_ctl_sock_exit(void)
6037c657876SArnaldo Carvalho de Melo {
6047c657876SArnaldo Carvalho de Melo 	if (dccp_ctl_socket != NULL)
6057c657876SArnaldo Carvalho de Melo 		sock_release(dccp_ctl_socket);
6067c657876SArnaldo Carvalho de Melo }
6077c657876SArnaldo Carvalho de Melo 
6087c657876SArnaldo Carvalho de Melo static int __init init_dccp_v4_mibs(void)
6097c657876SArnaldo Carvalho de Melo {
6107c657876SArnaldo Carvalho de Melo 	int rc = -ENOMEM;
6117c657876SArnaldo Carvalho de Melo 
6127c657876SArnaldo Carvalho de Melo 	dccp_statistics[0] = alloc_percpu(struct dccp_mib);
6137c657876SArnaldo Carvalho de Melo 	if (dccp_statistics[0] == NULL)
6147c657876SArnaldo Carvalho de Melo 		goto out;
6157c657876SArnaldo Carvalho de Melo 
6167c657876SArnaldo Carvalho de Melo 	dccp_statistics[1] = alloc_percpu(struct dccp_mib);
6177c657876SArnaldo Carvalho de Melo 	if (dccp_statistics[1] == NULL)
6187c657876SArnaldo Carvalho de Melo 		goto out_free_one;
6197c657876SArnaldo Carvalho de Melo 
6207c657876SArnaldo Carvalho de Melo 	rc = 0;
6217c657876SArnaldo Carvalho de Melo out:
6227c657876SArnaldo Carvalho de Melo 	return rc;
6237c657876SArnaldo Carvalho de Melo out_free_one:
6247c657876SArnaldo Carvalho de Melo 	free_percpu(dccp_statistics[0]);
6257c657876SArnaldo Carvalho de Melo 	dccp_statistics[0] = NULL;
6267c657876SArnaldo Carvalho de Melo 	goto out;
6277c657876SArnaldo Carvalho de Melo 
6287c657876SArnaldo Carvalho de Melo }
6297c657876SArnaldo Carvalho de Melo 
6307c657876SArnaldo Carvalho de Melo static int thash_entries;
6317c657876SArnaldo Carvalho de Melo module_param(thash_entries, int, 0444);
6327c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
6337c657876SArnaldo Carvalho de Melo 
6347c657876SArnaldo Carvalho de Melo int dccp_debug;
6357c657876SArnaldo Carvalho de Melo module_param(dccp_debug, int, 0444);
6367c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(dccp_debug, "Enable debug messages");
6377c657876SArnaldo Carvalho de Melo 
6387c657876SArnaldo Carvalho de Melo static int __init dccp_init(void)
6397c657876SArnaldo Carvalho de Melo {
6407c657876SArnaldo Carvalho de Melo 	unsigned long goal;
6417c657876SArnaldo Carvalho de Melo 	int ehash_order, bhash_order, i;
6427c657876SArnaldo Carvalho de Melo 	int rc = proto_register(&dccp_v4_prot, 1);
6437c657876SArnaldo Carvalho de Melo 
6447c657876SArnaldo Carvalho de Melo 	if (rc)
6457c657876SArnaldo Carvalho de Melo 		goto out;
6467c657876SArnaldo Carvalho de Melo 
647*7690af3fSArnaldo Carvalho de Melo 	dccp_hashinfo.bind_bucket_cachep =
648*7690af3fSArnaldo Carvalho de Melo 		kmem_cache_create("dccp_bind_bucket",
649*7690af3fSArnaldo Carvalho de Melo 				  sizeof(struct inet_bind_bucket), 0,
650*7690af3fSArnaldo Carvalho de Melo 				  SLAB_HWCACHE_ALIGN, NULL, NULL);
6517c657876SArnaldo Carvalho de Melo 	if (!dccp_hashinfo.bind_bucket_cachep)
6527c657876SArnaldo Carvalho de Melo 		goto out_proto_unregister;
6537c657876SArnaldo Carvalho de Melo 
6547c657876SArnaldo Carvalho de Melo 	/*
6557c657876SArnaldo Carvalho de Melo 	 * Size and allocate the main established and bind bucket
6567c657876SArnaldo Carvalho de Melo 	 * hash tables.
6577c657876SArnaldo Carvalho de Melo 	 *
6587c657876SArnaldo Carvalho de Melo 	 * The methodology is similar to that of the buffer cache.
6597c657876SArnaldo Carvalho de Melo 	 */
6607c657876SArnaldo Carvalho de Melo 	if (num_physpages >= (128 * 1024))
6617c657876SArnaldo Carvalho de Melo 		goal = num_physpages >> (21 - PAGE_SHIFT);
6627c657876SArnaldo Carvalho de Melo 	else
6637c657876SArnaldo Carvalho de Melo 		goal = num_physpages >> (23 - PAGE_SHIFT);
6647c657876SArnaldo Carvalho de Melo 
6657c657876SArnaldo Carvalho de Melo 	if (thash_entries)
666*7690af3fSArnaldo Carvalho de Melo 		goal = (thash_entries *
667*7690af3fSArnaldo Carvalho de Melo 			sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT;
6687c657876SArnaldo Carvalho de Melo 	for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++)
6697c657876SArnaldo Carvalho de Melo 		;
6707c657876SArnaldo Carvalho de Melo 	do {
6717c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE /
6727c657876SArnaldo Carvalho de Melo 					sizeof(struct inet_ehash_bucket);
6737c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.ehash_size >>= 1;
674*7690af3fSArnaldo Carvalho de Melo 		while (dccp_hashinfo.ehash_size &
675*7690af3fSArnaldo Carvalho de Melo 		       (dccp_hashinfo.ehash_size - 1))
6767c657876SArnaldo Carvalho de Melo 			dccp_hashinfo.ehash_size--;
6777c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.ehash = (struct inet_ehash_bucket *)
6787c657876SArnaldo Carvalho de Melo 			__get_free_pages(GFP_ATOMIC, ehash_order);
6797c657876SArnaldo Carvalho de Melo 	} while (!dccp_hashinfo.ehash && --ehash_order > 0);
6807c657876SArnaldo Carvalho de Melo 
6817c657876SArnaldo Carvalho de Melo 	if (!dccp_hashinfo.ehash) {
6827c657876SArnaldo Carvalho de Melo 		printk(KERN_CRIT "Failed to allocate DCCP "
6837c657876SArnaldo Carvalho de Melo 				 "established hash table\n");
6847c657876SArnaldo Carvalho de Melo 		goto out_free_bind_bucket_cachep;
6857c657876SArnaldo Carvalho de Melo 	}
6867c657876SArnaldo Carvalho de Melo 
6877c657876SArnaldo Carvalho de Melo 	for (i = 0; i < (dccp_hashinfo.ehash_size << 1); i++) {
6887c657876SArnaldo Carvalho de Melo 		rwlock_init(&dccp_hashinfo.ehash[i].lock);
6897c657876SArnaldo Carvalho de Melo 		INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain);
6907c657876SArnaldo Carvalho de Melo 	}
6917c657876SArnaldo Carvalho de Melo 
6927c657876SArnaldo Carvalho de Melo 	bhash_order = ehash_order;
6937c657876SArnaldo Carvalho de Melo 
6947c657876SArnaldo Carvalho de Melo 	do {
6957c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE /
6967c657876SArnaldo Carvalho de Melo 					sizeof(struct inet_bind_hashbucket);
697*7690af3fSArnaldo Carvalho de Melo 		if ((dccp_hashinfo.bhash_size > (64 * 1024)) &&
698*7690af3fSArnaldo Carvalho de Melo 		    bhash_order > 0)
6997c657876SArnaldo Carvalho de Melo 			continue;
7007c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.bhash = (struct inet_bind_hashbucket *)
7017c657876SArnaldo Carvalho de Melo 			__get_free_pages(GFP_ATOMIC, bhash_order);
7027c657876SArnaldo Carvalho de Melo 	} while (!dccp_hashinfo.bhash && --bhash_order >= 0);
7037c657876SArnaldo Carvalho de Melo 
7047c657876SArnaldo Carvalho de Melo 	if (!dccp_hashinfo.bhash) {
7057c657876SArnaldo Carvalho de Melo 		printk(KERN_CRIT "Failed to allocate DCCP bind hash table\n");
7067c657876SArnaldo Carvalho de Melo 		goto out_free_dccp_ehash;
7077c657876SArnaldo Carvalho de Melo 	}
7087c657876SArnaldo Carvalho de Melo 
7097c657876SArnaldo Carvalho de Melo 	for (i = 0; i < dccp_hashinfo.bhash_size; i++) {
7107c657876SArnaldo Carvalho de Melo 		spin_lock_init(&dccp_hashinfo.bhash[i].lock);
7117c657876SArnaldo Carvalho de Melo 		INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain);
7127c657876SArnaldo Carvalho de Melo 	}
7137c657876SArnaldo Carvalho de Melo 
7147c657876SArnaldo Carvalho de Melo 	if (init_dccp_v4_mibs())
7157c657876SArnaldo Carvalho de Melo 		goto out_free_dccp_bhash;
7167c657876SArnaldo Carvalho de Melo 
7177c657876SArnaldo Carvalho de Melo 	rc = -EAGAIN;
7187c657876SArnaldo Carvalho de Melo 	if (inet_add_protocol(&dccp_protocol, IPPROTO_DCCP))
7197c657876SArnaldo Carvalho de Melo 		goto out_free_dccp_v4_mibs;
7207c657876SArnaldo Carvalho de Melo 
7217c657876SArnaldo Carvalho de Melo 	inet_register_protosw(&dccp_v4_protosw);
7227c657876SArnaldo Carvalho de Melo 
7237c657876SArnaldo Carvalho de Melo 	rc = dccp_ctl_sock_init();
7247c657876SArnaldo Carvalho de Melo 	if (rc)
7257c657876SArnaldo Carvalho de Melo 		goto out_unregister_protosw;
7267c657876SArnaldo Carvalho de Melo out:
7277c657876SArnaldo Carvalho de Melo 	return rc;
7287c657876SArnaldo Carvalho de Melo out_unregister_protosw:
7297c657876SArnaldo Carvalho de Melo 	inet_unregister_protosw(&dccp_v4_protosw);
7307c657876SArnaldo Carvalho de Melo 	inet_del_protocol(&dccp_protocol, IPPROTO_DCCP);
7317c657876SArnaldo Carvalho de Melo out_free_dccp_v4_mibs:
7327c657876SArnaldo Carvalho de Melo 	free_percpu(dccp_statistics[0]);
7337c657876SArnaldo Carvalho de Melo 	free_percpu(dccp_statistics[1]);
7347c657876SArnaldo Carvalho de Melo 	dccp_statistics[0] = dccp_statistics[1] = NULL;
7357c657876SArnaldo Carvalho de Melo out_free_dccp_bhash:
7367c657876SArnaldo Carvalho de Melo 	free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
7377c657876SArnaldo Carvalho de Melo 	dccp_hashinfo.bhash = NULL;
7387c657876SArnaldo Carvalho de Melo out_free_dccp_ehash:
7397c657876SArnaldo Carvalho de Melo 	free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order);
7407c657876SArnaldo Carvalho de Melo 	dccp_hashinfo.ehash = NULL;
7417c657876SArnaldo Carvalho de Melo out_free_bind_bucket_cachep:
7427c657876SArnaldo Carvalho de Melo 	kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
7437c657876SArnaldo Carvalho de Melo 	dccp_hashinfo.bind_bucket_cachep = NULL;
7447c657876SArnaldo Carvalho de Melo out_proto_unregister:
7457c657876SArnaldo Carvalho de Melo 	proto_unregister(&dccp_v4_prot);
7467c657876SArnaldo Carvalho de Melo 	goto out;
7477c657876SArnaldo Carvalho de Melo }
7487c657876SArnaldo Carvalho de Melo 
7497c657876SArnaldo Carvalho de Melo static const char dccp_del_proto_err_msg[] __exitdata =
7507c657876SArnaldo Carvalho de Melo 	KERN_ERR "can't remove dccp net_protocol\n";
7517c657876SArnaldo Carvalho de Melo 
7527c657876SArnaldo Carvalho de Melo static void __exit dccp_fini(void)
7537c657876SArnaldo Carvalho de Melo {
7547c657876SArnaldo Carvalho de Melo 	dccp_ctl_sock_exit();
7557c657876SArnaldo Carvalho de Melo 
7567c657876SArnaldo Carvalho de Melo 	inet_unregister_protosw(&dccp_v4_protosw);
7577c657876SArnaldo Carvalho de Melo 
7587c657876SArnaldo Carvalho de Melo 	if (inet_del_protocol(&dccp_protocol, IPPROTO_DCCP) < 0)
7597c657876SArnaldo Carvalho de Melo 		printk(dccp_del_proto_err_msg);
7607c657876SArnaldo Carvalho de Melo 
7617c657876SArnaldo Carvalho de Melo 	/* Free the control endpoint.  */
7627c657876SArnaldo Carvalho de Melo 	sock_release(dccp_ctl_socket);
7637c657876SArnaldo Carvalho de Melo 
7647c657876SArnaldo Carvalho de Melo 	proto_unregister(&dccp_v4_prot);
7657c657876SArnaldo Carvalho de Melo 
7667c657876SArnaldo Carvalho de Melo 	kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
7677c657876SArnaldo Carvalho de Melo }
7687c657876SArnaldo Carvalho de Melo 
7697c657876SArnaldo Carvalho de Melo module_init(dccp_init);
7707c657876SArnaldo Carvalho de Melo module_exit(dccp_fini);
7717c657876SArnaldo Carvalho de Melo 
772bb97d31fSArnaldo Carvalho de Melo /*
773bb97d31fSArnaldo Carvalho de Melo  * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33)
774bb97d31fSArnaldo Carvalho de Melo  * values directly, Also cover the case where the protocol is not specified,
775bb97d31fSArnaldo Carvalho de Melo  * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP
776bb97d31fSArnaldo Carvalho de Melo  */
777bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-33-type-6");
778bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-0-type-6");
7797c657876SArnaldo Carvalho de Melo MODULE_LICENSE("GPL");
7807c657876SArnaldo Carvalho de Melo MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>");
7817c657876SArnaldo Carvalho de Melo MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol");
782