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