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