xref: /openbmc/linux/net/dccp/proto.c (revision 7c657876b63cb1d8a2ec06f8fc6c37bb8412e66c)
1*7c657876SArnaldo Carvalho de Melo /*
2*7c657876SArnaldo Carvalho de Melo  *  net/dccp/proto.c
3*7c657876SArnaldo Carvalho de Melo  *
4*7c657876SArnaldo Carvalho de Melo  *  An implementation of the DCCP protocol
5*7c657876SArnaldo Carvalho de Melo  *  Arnaldo Carvalho de Melo <acme@conectiva.com.br>
6*7c657876SArnaldo Carvalho de Melo  *
7*7c657876SArnaldo Carvalho de Melo  *	This program is free software; you can redistribute it and/or modify it
8*7c657876SArnaldo Carvalho de Melo  *	under the terms of the GNU General Public License version 2 as
9*7c657876SArnaldo Carvalho de Melo  *	published by the Free Software Foundation.
10*7c657876SArnaldo Carvalho de Melo  */
11*7c657876SArnaldo Carvalho de Melo 
12*7c657876SArnaldo Carvalho de Melo #include <linux/config.h>
13*7c657876SArnaldo Carvalho de Melo #include <linux/dccp.h>
14*7c657876SArnaldo Carvalho de Melo #include <linux/module.h>
15*7c657876SArnaldo Carvalho de Melo #include <linux/types.h>
16*7c657876SArnaldo Carvalho de Melo #include <linux/sched.h>
17*7c657876SArnaldo Carvalho de Melo #include <linux/kernel.h>
18*7c657876SArnaldo Carvalho de Melo #include <linux/skbuff.h>
19*7c657876SArnaldo Carvalho de Melo #include <linux/netdevice.h>
20*7c657876SArnaldo Carvalho de Melo #include <linux/in.h>
21*7c657876SArnaldo Carvalho de Melo #include <linux/if_arp.h>
22*7c657876SArnaldo Carvalho de Melo #include <linux/init.h>
23*7c657876SArnaldo Carvalho de Melo #include <linux/random.h>
24*7c657876SArnaldo Carvalho de Melo #include <net/checksum.h>
25*7c657876SArnaldo Carvalho de Melo 
26*7c657876SArnaldo Carvalho de Melo #include <net/inet_common.h>
27*7c657876SArnaldo Carvalho de Melo #include <net/ip.h>
28*7c657876SArnaldo Carvalho de Melo #include <net/protocol.h>
29*7c657876SArnaldo Carvalho de Melo #include <net/sock.h>
30*7c657876SArnaldo Carvalho de Melo #include <net/xfrm.h>
31*7c657876SArnaldo Carvalho de Melo 
32*7c657876SArnaldo Carvalho de Melo #include <asm/semaphore.h>
33*7c657876SArnaldo Carvalho de Melo #include <linux/spinlock.h>
34*7c657876SArnaldo Carvalho de Melo #include <linux/timer.h>
35*7c657876SArnaldo Carvalho de Melo #include <linux/delay.h>
36*7c657876SArnaldo Carvalho de Melo #include <linux/poll.h>
37*7c657876SArnaldo Carvalho de Melo #include <linux/dccp.h>
38*7c657876SArnaldo Carvalho de Melo 
39*7c657876SArnaldo Carvalho de Melo #include "ccid.h"
40*7c657876SArnaldo Carvalho de Melo #include "dccp.h"
41*7c657876SArnaldo Carvalho de Melo 
42*7c657876SArnaldo Carvalho de Melo DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics);
43*7c657876SArnaldo Carvalho de Melo 
44*7c657876SArnaldo Carvalho de Melo atomic_t dccp_orphan_count = ATOMIC_INIT(0);
45*7c657876SArnaldo Carvalho de Melo 
46*7c657876SArnaldo Carvalho de Melo static struct net_protocol dccp_protocol = {
47*7c657876SArnaldo Carvalho de Melo 	.handler	= dccp_v4_rcv,
48*7c657876SArnaldo Carvalho de Melo 	.err_handler	= dccp_v4_err,
49*7c657876SArnaldo Carvalho de Melo };
50*7c657876SArnaldo Carvalho de Melo 
51*7c657876SArnaldo Carvalho de Melo const char *dccp_packet_name(const int type)
52*7c657876SArnaldo Carvalho de Melo {
53*7c657876SArnaldo Carvalho de Melo 	static const char *dccp_packet_names[] = {
54*7c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_REQUEST]  = "REQUEST",
55*7c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_RESPONSE] = "RESPONSE",
56*7c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_DATA]	    = "DATA",
57*7c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_ACK]	    = "ACK",
58*7c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_DATAACK]  = "DATAACK",
59*7c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_CLOSEREQ] = "CLOSEREQ",
60*7c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_CLOSE]    = "CLOSE",
61*7c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_RESET]    = "RESET",
62*7c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_SYNC]	    = "SYNC",
63*7c657876SArnaldo Carvalho de Melo 		[DCCP_PKT_SYNCACK]  = "SYNCACK",
64*7c657876SArnaldo Carvalho de Melo 	};
65*7c657876SArnaldo Carvalho de Melo 
66*7c657876SArnaldo Carvalho de Melo 	if (type >= DCCP_NR_PKT_TYPES)
67*7c657876SArnaldo Carvalho de Melo 		return "INVALID";
68*7c657876SArnaldo Carvalho de Melo 	else
69*7c657876SArnaldo Carvalho de Melo 		return dccp_packet_names[type];
70*7c657876SArnaldo Carvalho de Melo }
71*7c657876SArnaldo Carvalho de Melo 
72*7c657876SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_packet_name);
73*7c657876SArnaldo Carvalho de Melo 
74*7c657876SArnaldo Carvalho de Melo const char *dccp_state_name(const int state)
75*7c657876SArnaldo Carvalho de Melo {
76*7c657876SArnaldo Carvalho de Melo 	static char *dccp_state_names[] = {
77*7c657876SArnaldo Carvalho de Melo 	[DCCP_OPEN]	  = "OPEN",
78*7c657876SArnaldo Carvalho de Melo 	[DCCP_REQUESTING] = "REQUESTING",
79*7c657876SArnaldo Carvalho de Melo 	[DCCP_PARTOPEN]	  = "PARTOPEN",
80*7c657876SArnaldo Carvalho de Melo 	[DCCP_LISTEN]	  = "LISTEN",
81*7c657876SArnaldo Carvalho de Melo 	[DCCP_RESPOND]	  = "RESPOND",
82*7c657876SArnaldo Carvalho de Melo 	[DCCP_CLOSING]	  = "CLOSING",
83*7c657876SArnaldo Carvalho de Melo 	[DCCP_TIME_WAIT]  = "TIME_WAIT",
84*7c657876SArnaldo Carvalho de Melo 	[DCCP_CLOSED]	  = "CLOSED",
85*7c657876SArnaldo Carvalho de Melo 	};
86*7c657876SArnaldo Carvalho de Melo 
87*7c657876SArnaldo Carvalho de Melo 	if (state >= DCCP_MAX_STATES)
88*7c657876SArnaldo Carvalho de Melo 		return "INVALID STATE!";
89*7c657876SArnaldo Carvalho de Melo 	else
90*7c657876SArnaldo Carvalho de Melo 		return dccp_state_names[state];
91*7c657876SArnaldo Carvalho de Melo }
92*7c657876SArnaldo Carvalho de Melo 
93*7c657876SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_state_name);
94*7c657876SArnaldo Carvalho de Melo 
95*7c657876SArnaldo Carvalho de Melo static inline int dccp_listen_start(struct sock *sk)
96*7c657876SArnaldo Carvalho de Melo {
97*7c657876SArnaldo Carvalho de Melo 	dccp_sk(sk)->dccps_role = DCCP_ROLE_LISTEN;
98*7c657876SArnaldo Carvalho de Melo 	return inet_csk_listen_start(sk, TCP_SYNQ_HSIZE);
99*7c657876SArnaldo Carvalho de Melo }
100*7c657876SArnaldo Carvalho de Melo 
101*7c657876SArnaldo Carvalho de Melo int dccp_disconnect(struct sock *sk, int flags)
102*7c657876SArnaldo Carvalho de Melo {
103*7c657876SArnaldo Carvalho de Melo 	struct inet_connection_sock *icsk = inet_csk(sk);
104*7c657876SArnaldo Carvalho de Melo 	struct inet_sock *inet = inet_sk(sk);
105*7c657876SArnaldo Carvalho de Melo 	int err = 0;
106*7c657876SArnaldo Carvalho de Melo 	const int old_state = sk->sk_state;
107*7c657876SArnaldo Carvalho de Melo 
108*7c657876SArnaldo Carvalho de Melo 	if (old_state != DCCP_CLOSED)
109*7c657876SArnaldo Carvalho de Melo 		dccp_set_state(sk, DCCP_CLOSED);
110*7c657876SArnaldo Carvalho de Melo 
111*7c657876SArnaldo Carvalho de Melo 	/* ABORT function of RFC793 */
112*7c657876SArnaldo Carvalho de Melo 	if (old_state == DCCP_LISTEN) {
113*7c657876SArnaldo Carvalho de Melo 		inet_csk_listen_stop(sk);
114*7c657876SArnaldo Carvalho de Melo 	/* FIXME: do the active reset thing */
115*7c657876SArnaldo Carvalho de Melo 	} else if (old_state == DCCP_REQUESTING)
116*7c657876SArnaldo Carvalho de Melo 		sk->sk_err = ECONNRESET;
117*7c657876SArnaldo Carvalho de Melo 
118*7c657876SArnaldo Carvalho de Melo 	dccp_clear_xmit_timers(sk);
119*7c657876SArnaldo Carvalho de Melo 	__skb_queue_purge(&sk->sk_receive_queue);
120*7c657876SArnaldo Carvalho de Melo 	if (sk->sk_send_head != NULL) {
121*7c657876SArnaldo Carvalho de Melo 		__kfree_skb(sk->sk_send_head);
122*7c657876SArnaldo Carvalho de Melo 		sk->sk_send_head = NULL;
123*7c657876SArnaldo Carvalho de Melo 	}
124*7c657876SArnaldo Carvalho de Melo 
125*7c657876SArnaldo Carvalho de Melo 	inet->dport = 0;
126*7c657876SArnaldo Carvalho de Melo 
127*7c657876SArnaldo Carvalho de Melo 	if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
128*7c657876SArnaldo Carvalho de Melo 		inet_reset_saddr(sk);
129*7c657876SArnaldo Carvalho de Melo 
130*7c657876SArnaldo Carvalho de Melo 	sk->sk_shutdown = 0;
131*7c657876SArnaldo Carvalho de Melo 	sock_reset_flag(sk, SOCK_DONE);
132*7c657876SArnaldo Carvalho de Melo 
133*7c657876SArnaldo Carvalho de Melo 	icsk->icsk_backoff = 0;
134*7c657876SArnaldo Carvalho de Melo 	inet_csk_delack_init(sk);
135*7c657876SArnaldo Carvalho de Melo 	__sk_dst_reset(sk);
136*7c657876SArnaldo Carvalho de Melo 
137*7c657876SArnaldo Carvalho de Melo 	BUG_TRAP(!inet->num || icsk->icsk_bind_hash);
138*7c657876SArnaldo Carvalho de Melo 
139*7c657876SArnaldo Carvalho de Melo 	sk->sk_error_report(sk);
140*7c657876SArnaldo Carvalho de Melo 	return err;
141*7c657876SArnaldo Carvalho de Melo }
142*7c657876SArnaldo Carvalho de Melo 
143*7c657876SArnaldo Carvalho de Melo int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
144*7c657876SArnaldo Carvalho de Melo {
145*7c657876SArnaldo Carvalho de Melo 	dccp_pr_debug("entry\n");
146*7c657876SArnaldo Carvalho de Melo 	return -ENOIOCTLCMD;
147*7c657876SArnaldo Carvalho de Melo }
148*7c657876SArnaldo Carvalho de Melo 
149*7c657876SArnaldo Carvalho de Melo int dccp_setsockopt(struct sock *sk, int level, int optname,
150*7c657876SArnaldo Carvalho de Melo 		    char *optval, int optlen)
151*7c657876SArnaldo Carvalho de Melo {
152*7c657876SArnaldo Carvalho de Melo 	dccp_pr_debug("entry\n");
153*7c657876SArnaldo Carvalho de Melo 
154*7c657876SArnaldo Carvalho de Melo 	if (level != SOL_DCCP)
155*7c657876SArnaldo Carvalho de Melo 		return ip_setsockopt(sk, level, optname, optval, optlen);
156*7c657876SArnaldo Carvalho de Melo 
157*7c657876SArnaldo Carvalho de Melo 	return -EOPNOTSUPP;
158*7c657876SArnaldo Carvalho de Melo }
159*7c657876SArnaldo Carvalho de Melo 
160*7c657876SArnaldo Carvalho de Melo int dccp_getsockopt(struct sock *sk, int level, int optname,
161*7c657876SArnaldo Carvalho de Melo 		    char *optval, int *optlen)
162*7c657876SArnaldo Carvalho de Melo {
163*7c657876SArnaldo Carvalho de Melo 	dccp_pr_debug("entry\n");
164*7c657876SArnaldo Carvalho de Melo 
165*7c657876SArnaldo Carvalho de Melo 	if (level != SOL_DCCP)
166*7c657876SArnaldo Carvalho de Melo 		return ip_getsockopt(sk, level, optname, optval, optlen);
167*7c657876SArnaldo Carvalho de Melo 
168*7c657876SArnaldo Carvalho de Melo 	return -EOPNOTSUPP;
169*7c657876SArnaldo Carvalho de Melo }
170*7c657876SArnaldo Carvalho de Melo 
171*7c657876SArnaldo Carvalho de Melo int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
172*7c657876SArnaldo Carvalho de Melo 		 size_t len)
173*7c657876SArnaldo Carvalho de Melo {
174*7c657876SArnaldo Carvalho de Melo 	const struct dccp_sock *dp = dccp_sk(sk);
175*7c657876SArnaldo Carvalho de Melo 	const int flags = msg->msg_flags;
176*7c657876SArnaldo Carvalho de Melo 	const int noblock = flags & MSG_DONTWAIT;
177*7c657876SArnaldo Carvalho de Melo 	struct sk_buff *skb;
178*7c657876SArnaldo Carvalho de Melo 	int rc, size;
179*7c657876SArnaldo Carvalho de Melo 	long timeo;
180*7c657876SArnaldo Carvalho de Melo 
181*7c657876SArnaldo Carvalho de Melo 	if (len > dp->dccps_mss_cache)
182*7c657876SArnaldo Carvalho de Melo 		return -EMSGSIZE;
183*7c657876SArnaldo Carvalho de Melo 
184*7c657876SArnaldo Carvalho de Melo 	lock_sock(sk);
185*7c657876SArnaldo Carvalho de Melo 
186*7c657876SArnaldo Carvalho de Melo 	timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
187*7c657876SArnaldo Carvalho de Melo 
188*7c657876SArnaldo Carvalho de Melo 	/*
189*7c657876SArnaldo Carvalho de Melo 	 * We have to use sk_stream_wait_connect here to set sk_write_pending,
190*7c657876SArnaldo Carvalho de Melo 	 * so that the trick in dccp_rcv_request_sent_state_process.
191*7c657876SArnaldo Carvalho de Melo 	 */
192*7c657876SArnaldo Carvalho de Melo 	/* Wait for a connection to finish. */
193*7c657876SArnaldo Carvalho de Melo 	if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN | DCCPF_CLOSING))
194*7c657876SArnaldo Carvalho de Melo 		if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0)
195*7c657876SArnaldo Carvalho de Melo 			goto out_err;
196*7c657876SArnaldo Carvalho de Melo 
197*7c657876SArnaldo Carvalho de Melo 	size = sk->sk_prot->max_header + len;
198*7c657876SArnaldo Carvalho de Melo 	release_sock(sk);
199*7c657876SArnaldo Carvalho de Melo 	skb = sock_alloc_send_skb(sk, size, noblock, &rc);
200*7c657876SArnaldo Carvalho de Melo 	lock_sock(sk);
201*7c657876SArnaldo Carvalho de Melo 
202*7c657876SArnaldo Carvalho de Melo 	if (skb == NULL)
203*7c657876SArnaldo Carvalho de Melo 		goto out_release;
204*7c657876SArnaldo Carvalho de Melo 
205*7c657876SArnaldo Carvalho de Melo 	skb_reserve(skb, sk->sk_prot->max_header);
206*7c657876SArnaldo Carvalho de Melo 	rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
207*7c657876SArnaldo Carvalho de Melo 	if (rc == 0) {
208*7c657876SArnaldo Carvalho de Melo 		struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
209*7c657876SArnaldo Carvalho de Melo 		const struct dccp_ackpkts *ap = dp->dccps_hc_rx_ackpkts;
210*7c657876SArnaldo Carvalho de Melo 		long delay;
211*7c657876SArnaldo Carvalho de Melo 
212*7c657876SArnaldo Carvalho de Melo 		/*
213*7c657876SArnaldo Carvalho de Melo 		 * XXX: This is just to match the Waikato tree CA interaction
214*7c657876SArnaldo Carvalho de Melo 		 * points, after the CCID3 code is stable and I have a better
215*7c657876SArnaldo Carvalho de Melo 		 * understanding of behaviour I'll change this to look more like
216*7c657876SArnaldo Carvalho de Melo 		 * TCP.
217*7c657876SArnaldo Carvalho de Melo 		 */
218*7c657876SArnaldo Carvalho de Melo 		while (1) {
219*7c657876SArnaldo Carvalho de Melo 			rc = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk,
220*7c657876SArnaldo Carvalho de Melo 						    skb, len, &delay);
221*7c657876SArnaldo Carvalho de Melo 			if (rc == 0)
222*7c657876SArnaldo Carvalho de Melo 				break;
223*7c657876SArnaldo Carvalho de Melo 			if (rc != -EAGAIN)
224*7c657876SArnaldo Carvalho de Melo 				goto out_discard;
225*7c657876SArnaldo Carvalho de Melo 			if (delay > timeo)
226*7c657876SArnaldo Carvalho de Melo 				goto out_discard;
227*7c657876SArnaldo Carvalho de Melo 			release_sock(sk);
228*7c657876SArnaldo Carvalho de Melo 			delay = schedule_timeout(delay);
229*7c657876SArnaldo Carvalho de Melo 			lock_sock(sk);
230*7c657876SArnaldo Carvalho de Melo 			timeo -= delay;
231*7c657876SArnaldo Carvalho de Melo 			if (signal_pending(current))
232*7c657876SArnaldo Carvalho de Melo 				goto out_interrupted;
233*7c657876SArnaldo Carvalho de Melo 			rc = -EPIPE;
234*7c657876SArnaldo Carvalho de Melo 			if (!(sk->sk_state == DCCP_PARTOPEN || sk->sk_state == DCCP_OPEN))
235*7c657876SArnaldo Carvalho de Melo 				goto out_discard;
236*7c657876SArnaldo Carvalho de Melo 		}
237*7c657876SArnaldo Carvalho de Melo 
238*7c657876SArnaldo Carvalho de Melo 		if (sk->sk_state == DCCP_PARTOPEN) {
239*7c657876SArnaldo Carvalho de Melo 			/* See 8.1.5.  Handshake Completion */
240*7c657876SArnaldo Carvalho de Melo 			inet_csk_schedule_ack(sk);
241*7c657876SArnaldo Carvalho de Melo 			inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, inet_csk(sk)->icsk_rto, TCP_RTO_MAX);
242*7c657876SArnaldo Carvalho de Melo 			dcb->dccpd_type = DCCP_PKT_DATAACK;
243*7c657876SArnaldo Carvalho de Melo 			/* FIXME: we really should have a dccps_ack_pending or use icsk */
244*7c657876SArnaldo Carvalho de Melo 		} else if (inet_csk_ack_scheduled(sk) ||
245*7c657876SArnaldo Carvalho de Melo 			   (dp->dccps_options.dccpo_send_ack_vector &&
246*7c657876SArnaldo Carvalho de Melo 			    ap->dccpap_buf_ackno != DCCP_MAX_SEQNO + 1 &&
247*7c657876SArnaldo Carvalho de Melo 			    ap->dccpap_ack_seqno == DCCP_MAX_SEQNO + 1))
248*7c657876SArnaldo Carvalho de Melo 			dcb->dccpd_type = DCCP_PKT_DATAACK;
249*7c657876SArnaldo Carvalho de Melo 		else
250*7c657876SArnaldo Carvalho de Melo 			dcb->dccpd_type = DCCP_PKT_DATA;
251*7c657876SArnaldo Carvalho de Melo 		dccp_transmit_skb(sk, skb);
252*7c657876SArnaldo Carvalho de Melo 		ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, 0, len);
253*7c657876SArnaldo Carvalho de Melo 	} else {
254*7c657876SArnaldo Carvalho de Melo out_discard:
255*7c657876SArnaldo Carvalho de Melo 		kfree_skb(skb);
256*7c657876SArnaldo Carvalho de Melo 	}
257*7c657876SArnaldo Carvalho de Melo out_release:
258*7c657876SArnaldo Carvalho de Melo 	release_sock(sk);
259*7c657876SArnaldo Carvalho de Melo 	return rc ? : len;
260*7c657876SArnaldo Carvalho de Melo out_err:
261*7c657876SArnaldo Carvalho de Melo 	rc = sk_stream_error(sk, flags, rc);
262*7c657876SArnaldo Carvalho de Melo 	goto out_release;
263*7c657876SArnaldo Carvalho de Melo out_interrupted:
264*7c657876SArnaldo Carvalho de Melo 	rc = sock_intr_errno(timeo);
265*7c657876SArnaldo Carvalho de Melo 	goto out_discard;
266*7c657876SArnaldo Carvalho de Melo }
267*7c657876SArnaldo Carvalho de Melo 
268*7c657876SArnaldo Carvalho de Melo EXPORT_SYMBOL(dccp_sendmsg);
269*7c657876SArnaldo Carvalho de Melo 
270*7c657876SArnaldo Carvalho de Melo int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
271*7c657876SArnaldo Carvalho de Melo 		 size_t len, int nonblock, int flags, int *addr_len)
272*7c657876SArnaldo Carvalho de Melo {
273*7c657876SArnaldo Carvalho de Melo 	const struct dccp_hdr *dh;
274*7c657876SArnaldo Carvalho de Melo 	int copied = 0;
275*7c657876SArnaldo Carvalho de Melo 	unsigned long used;
276*7c657876SArnaldo Carvalho de Melo 	int err;
277*7c657876SArnaldo Carvalho de Melo 	int target;		/* Read at least this many bytes */
278*7c657876SArnaldo Carvalho de Melo 	long timeo;
279*7c657876SArnaldo Carvalho de Melo 
280*7c657876SArnaldo Carvalho de Melo 	lock_sock(sk);
281*7c657876SArnaldo Carvalho de Melo 
282*7c657876SArnaldo Carvalho de Melo 	err = -ENOTCONN;
283*7c657876SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_LISTEN)
284*7c657876SArnaldo Carvalho de Melo 		goto out;
285*7c657876SArnaldo Carvalho de Melo 
286*7c657876SArnaldo Carvalho de Melo 	timeo = sock_rcvtimeo(sk, nonblock);
287*7c657876SArnaldo Carvalho de Melo 
288*7c657876SArnaldo Carvalho de Melo 	/* Urgent data needs to be handled specially. */
289*7c657876SArnaldo Carvalho de Melo 	if (flags & MSG_OOB)
290*7c657876SArnaldo Carvalho de Melo 		goto recv_urg;
291*7c657876SArnaldo Carvalho de Melo 
292*7c657876SArnaldo Carvalho de Melo 	/* FIXME */
293*7c657876SArnaldo Carvalho de Melo #if 0
294*7c657876SArnaldo Carvalho de Melo 	seq = &tp->copied_seq;
295*7c657876SArnaldo Carvalho de Melo 	if (flags & MSG_PEEK) {
296*7c657876SArnaldo Carvalho de Melo 		peek_seq = tp->copied_seq;
297*7c657876SArnaldo Carvalho de Melo 		seq = &peek_seq;
298*7c657876SArnaldo Carvalho de Melo 	}
299*7c657876SArnaldo Carvalho de Melo #endif
300*7c657876SArnaldo Carvalho de Melo 
301*7c657876SArnaldo Carvalho de Melo 	target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
302*7c657876SArnaldo Carvalho de Melo 
303*7c657876SArnaldo Carvalho de Melo 	do {
304*7c657876SArnaldo Carvalho de Melo 		struct sk_buff *skb;
305*7c657876SArnaldo Carvalho de Melo 		u32 offset;
306*7c657876SArnaldo Carvalho de Melo 
307*7c657876SArnaldo Carvalho de Melo 	/* FIXME */
308*7c657876SArnaldo Carvalho de Melo #if 0
309*7c657876SArnaldo Carvalho de Melo 		/* Are we at urgent data? Stop if we have read anything or have SIGURG pending. */
310*7c657876SArnaldo Carvalho de Melo 		if (tp->urg_data && tp->urg_seq == *seq) {
311*7c657876SArnaldo Carvalho de Melo 			if (copied)
312*7c657876SArnaldo Carvalho de Melo 				break;
313*7c657876SArnaldo Carvalho de Melo 			if (signal_pending(current)) {
314*7c657876SArnaldo Carvalho de Melo 				copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;
315*7c657876SArnaldo Carvalho de Melo 				break;
316*7c657876SArnaldo Carvalho de Melo 			}
317*7c657876SArnaldo Carvalho de Melo 		}
318*7c657876SArnaldo Carvalho de Melo #endif
319*7c657876SArnaldo Carvalho de Melo 
320*7c657876SArnaldo Carvalho de Melo 		/* Next get a buffer. */
321*7c657876SArnaldo Carvalho de Melo 
322*7c657876SArnaldo Carvalho de Melo 		skb = skb_peek(&sk->sk_receive_queue);
323*7c657876SArnaldo Carvalho de Melo 		do {
324*7c657876SArnaldo Carvalho de Melo 			if (!skb)
325*7c657876SArnaldo Carvalho de Melo 				break;
326*7c657876SArnaldo Carvalho de Melo 
327*7c657876SArnaldo Carvalho de Melo 			offset = 0;
328*7c657876SArnaldo Carvalho de Melo 			dh = dccp_hdr(skb);
329*7c657876SArnaldo Carvalho de Melo 
330*7c657876SArnaldo Carvalho de Melo 			if (dh->dccph_type == DCCP_PKT_DATA ||
331*7c657876SArnaldo Carvalho de Melo 			    dh->dccph_type == DCCP_PKT_DATAACK)
332*7c657876SArnaldo Carvalho de Melo 				goto found_ok_skb;
333*7c657876SArnaldo Carvalho de Melo 
334*7c657876SArnaldo Carvalho de Melo 			if (dh->dccph_type == DCCP_PKT_RESET ||
335*7c657876SArnaldo Carvalho de Melo 			    dh->dccph_type == DCCP_PKT_CLOSE) {
336*7c657876SArnaldo Carvalho de Melo 				dccp_pr_debug("found fin ok!\n");
337*7c657876SArnaldo Carvalho de Melo 				goto found_fin_ok;
338*7c657876SArnaldo Carvalho de Melo 			}
339*7c657876SArnaldo Carvalho de Melo 			dccp_pr_debug("packet_type=%s\n", dccp_packet_name(dh->dccph_type));
340*7c657876SArnaldo Carvalho de Melo 			BUG_TRAP(flags & MSG_PEEK);
341*7c657876SArnaldo Carvalho de Melo 			skb = skb->next;
342*7c657876SArnaldo Carvalho de Melo 		} while (skb != (struct sk_buff *)&sk->sk_receive_queue);
343*7c657876SArnaldo Carvalho de Melo 
344*7c657876SArnaldo Carvalho de Melo 		/* Well, if we have backlog, try to process it now yet. */
345*7c657876SArnaldo Carvalho de Melo 		if (copied >= target && !sk->sk_backlog.tail)
346*7c657876SArnaldo Carvalho de Melo 			break;
347*7c657876SArnaldo Carvalho de Melo 
348*7c657876SArnaldo Carvalho de Melo 		if (copied) {
349*7c657876SArnaldo Carvalho de Melo 			if (sk->sk_err ||
350*7c657876SArnaldo Carvalho de Melo 			    sk->sk_state == DCCP_CLOSED ||
351*7c657876SArnaldo Carvalho de Melo 			    (sk->sk_shutdown & RCV_SHUTDOWN) ||
352*7c657876SArnaldo Carvalho de Melo 			    !timeo ||
353*7c657876SArnaldo Carvalho de Melo 			    signal_pending(current) ||
354*7c657876SArnaldo Carvalho de Melo 			    (flags & MSG_PEEK))
355*7c657876SArnaldo Carvalho de Melo 				break;
356*7c657876SArnaldo Carvalho de Melo 		} else {
357*7c657876SArnaldo Carvalho de Melo 			if (sock_flag(sk, SOCK_DONE))
358*7c657876SArnaldo Carvalho de Melo 				break;
359*7c657876SArnaldo Carvalho de Melo 
360*7c657876SArnaldo Carvalho de Melo 			if (sk->sk_err) {
361*7c657876SArnaldo Carvalho de Melo 				copied = sock_error(sk);
362*7c657876SArnaldo Carvalho de Melo 				break;
363*7c657876SArnaldo Carvalho de Melo 			}
364*7c657876SArnaldo Carvalho de Melo 
365*7c657876SArnaldo Carvalho de Melo 			if (sk->sk_shutdown & RCV_SHUTDOWN)
366*7c657876SArnaldo Carvalho de Melo 				break;
367*7c657876SArnaldo Carvalho de Melo 
368*7c657876SArnaldo Carvalho de Melo 			if (sk->sk_state == DCCP_CLOSED) {
369*7c657876SArnaldo Carvalho de Melo 				if (!sock_flag(sk, SOCK_DONE)) {
370*7c657876SArnaldo Carvalho de Melo 					/* This occurs when user tries to read
371*7c657876SArnaldo Carvalho de Melo 					 * from never connected socket.
372*7c657876SArnaldo Carvalho de Melo 					 */
373*7c657876SArnaldo Carvalho de Melo 					copied = -ENOTCONN;
374*7c657876SArnaldo Carvalho de Melo 					break;
375*7c657876SArnaldo Carvalho de Melo 				}
376*7c657876SArnaldo Carvalho de Melo 				break;
377*7c657876SArnaldo Carvalho de Melo 			}
378*7c657876SArnaldo Carvalho de Melo 
379*7c657876SArnaldo Carvalho de Melo 			if (!timeo) {
380*7c657876SArnaldo Carvalho de Melo 				copied = -EAGAIN;
381*7c657876SArnaldo Carvalho de Melo 				break;
382*7c657876SArnaldo Carvalho de Melo 			}
383*7c657876SArnaldo Carvalho de Melo 
384*7c657876SArnaldo Carvalho de Melo 			if (signal_pending(current)) {
385*7c657876SArnaldo Carvalho de Melo 				copied = sock_intr_errno(timeo);
386*7c657876SArnaldo Carvalho de Melo 				break;
387*7c657876SArnaldo Carvalho de Melo 			}
388*7c657876SArnaldo Carvalho de Melo 		}
389*7c657876SArnaldo Carvalho de Melo 
390*7c657876SArnaldo Carvalho de Melo 		/* FIXME: cleanup_rbuf(sk, copied); */
391*7c657876SArnaldo Carvalho de Melo 
392*7c657876SArnaldo Carvalho de Melo 		if (copied >= target) {
393*7c657876SArnaldo Carvalho de Melo 			/* Do not sleep, just process backlog. */
394*7c657876SArnaldo Carvalho de Melo 			release_sock(sk);
395*7c657876SArnaldo Carvalho de Melo 			lock_sock(sk);
396*7c657876SArnaldo Carvalho de Melo 		} else
397*7c657876SArnaldo Carvalho de Melo 			sk_wait_data(sk, &timeo);
398*7c657876SArnaldo Carvalho de Melo 
399*7c657876SArnaldo Carvalho de Melo 		continue;
400*7c657876SArnaldo Carvalho de Melo 
401*7c657876SArnaldo Carvalho de Melo 	found_ok_skb:
402*7c657876SArnaldo Carvalho de Melo 		/* Ok so how much can we use? */
403*7c657876SArnaldo Carvalho de Melo 		used = skb->len - offset;
404*7c657876SArnaldo Carvalho de Melo 		if (len < used)
405*7c657876SArnaldo Carvalho de Melo 			used = len;
406*7c657876SArnaldo Carvalho de Melo 
407*7c657876SArnaldo Carvalho de Melo 		if (!(flags & MSG_TRUNC)) {
408*7c657876SArnaldo Carvalho de Melo 			err = skb_copy_datagram_iovec(skb, offset,
409*7c657876SArnaldo Carvalho de Melo 						      msg->msg_iov, used);
410*7c657876SArnaldo Carvalho de Melo 			if (err) {
411*7c657876SArnaldo Carvalho de Melo 				/* Exception. Bailout! */
412*7c657876SArnaldo Carvalho de Melo 				if (!copied)
413*7c657876SArnaldo Carvalho de Melo 					copied = -EFAULT;
414*7c657876SArnaldo Carvalho de Melo 				break;
415*7c657876SArnaldo Carvalho de Melo 			}
416*7c657876SArnaldo Carvalho de Melo 		}
417*7c657876SArnaldo Carvalho de Melo 
418*7c657876SArnaldo Carvalho de Melo 		copied += used;
419*7c657876SArnaldo Carvalho de Melo 		len -= used;
420*7c657876SArnaldo Carvalho de Melo 
421*7c657876SArnaldo Carvalho de Melo 		/* FIXME: tcp_rcv_space_adjust(sk); */
422*7c657876SArnaldo Carvalho de Melo 
423*7c657876SArnaldo Carvalho de Melo //skip_copy:
424*7c657876SArnaldo Carvalho de Melo 		if (used + offset < skb->len)
425*7c657876SArnaldo Carvalho de Melo 			continue;
426*7c657876SArnaldo Carvalho de Melo 
427*7c657876SArnaldo Carvalho de Melo 		if (!(flags & MSG_PEEK))
428*7c657876SArnaldo Carvalho de Melo 			sk_eat_skb(sk, skb);
429*7c657876SArnaldo Carvalho de Melo 		continue;
430*7c657876SArnaldo Carvalho de Melo 	found_fin_ok:
431*7c657876SArnaldo Carvalho de Melo 		if (!(flags & MSG_PEEK))
432*7c657876SArnaldo Carvalho de Melo 			sk_eat_skb(sk, skb);
433*7c657876SArnaldo Carvalho de Melo 		break;
434*7c657876SArnaldo Carvalho de Melo 
435*7c657876SArnaldo Carvalho de Melo 	} while (len > 0);
436*7c657876SArnaldo Carvalho de Melo 
437*7c657876SArnaldo Carvalho de Melo 	/* According to UNIX98, msg_name/msg_namelen are ignored
438*7c657876SArnaldo Carvalho de Melo 	 * on connected socket. I was just happy when found this 8) --ANK
439*7c657876SArnaldo Carvalho de Melo 	 */
440*7c657876SArnaldo Carvalho de Melo 
441*7c657876SArnaldo Carvalho de Melo 	/* Clean up data we have read: This will do ACK frames. */
442*7c657876SArnaldo Carvalho de Melo 	/* FIXME: cleanup_rbuf(sk, copied); */
443*7c657876SArnaldo Carvalho de Melo 
444*7c657876SArnaldo Carvalho de Melo 	release_sock(sk);
445*7c657876SArnaldo Carvalho de Melo 	return copied;
446*7c657876SArnaldo Carvalho de Melo 
447*7c657876SArnaldo Carvalho de Melo out:
448*7c657876SArnaldo Carvalho de Melo 	release_sock(sk);
449*7c657876SArnaldo Carvalho de Melo 	return err;
450*7c657876SArnaldo Carvalho de Melo 
451*7c657876SArnaldo Carvalho de Melo recv_urg:
452*7c657876SArnaldo Carvalho de Melo 	/* FIXME: err = tcp_recv_urg(sk, timeo, msg, len, flags, addr_len); */
453*7c657876SArnaldo Carvalho de Melo 	goto out;
454*7c657876SArnaldo Carvalho de Melo }
455*7c657876SArnaldo Carvalho de Melo 
456*7c657876SArnaldo Carvalho de Melo static int inet_dccp_listen(struct socket *sock, int backlog)
457*7c657876SArnaldo Carvalho de Melo {
458*7c657876SArnaldo Carvalho de Melo 	struct sock *sk = sock->sk;
459*7c657876SArnaldo Carvalho de Melo 	unsigned char old_state;
460*7c657876SArnaldo Carvalho de Melo 	int err;
461*7c657876SArnaldo Carvalho de Melo 
462*7c657876SArnaldo Carvalho de Melo 	lock_sock(sk);
463*7c657876SArnaldo Carvalho de Melo 
464*7c657876SArnaldo Carvalho de Melo 	err = -EINVAL;
465*7c657876SArnaldo Carvalho de Melo 	if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP)
466*7c657876SArnaldo Carvalho de Melo 		goto out;
467*7c657876SArnaldo Carvalho de Melo 
468*7c657876SArnaldo Carvalho de Melo 	old_state = sk->sk_state;
469*7c657876SArnaldo Carvalho de Melo 	if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN)))
470*7c657876SArnaldo Carvalho de Melo 		goto out;
471*7c657876SArnaldo Carvalho de Melo 
472*7c657876SArnaldo Carvalho de Melo 	/* Really, if the socket is already in listen state
473*7c657876SArnaldo Carvalho de Melo 	 * we can only allow the backlog to be adjusted.
474*7c657876SArnaldo Carvalho de Melo 	 */
475*7c657876SArnaldo Carvalho de Melo 	if (old_state != DCCP_LISTEN) {
476*7c657876SArnaldo Carvalho de Melo 		/*
477*7c657876SArnaldo Carvalho de Melo 		 * FIXME: here it probably should be sk->sk_prot->listen_start
478*7c657876SArnaldo Carvalho de Melo 		 * see tcp_listen_start
479*7c657876SArnaldo Carvalho de Melo 		 */
480*7c657876SArnaldo Carvalho de Melo 		err = dccp_listen_start(sk);
481*7c657876SArnaldo Carvalho de Melo 		if (err)
482*7c657876SArnaldo Carvalho de Melo 			goto out;
483*7c657876SArnaldo Carvalho de Melo 	}
484*7c657876SArnaldo Carvalho de Melo 	sk->sk_max_ack_backlog = backlog;
485*7c657876SArnaldo Carvalho de Melo 	err = 0;
486*7c657876SArnaldo Carvalho de Melo 
487*7c657876SArnaldo Carvalho de Melo out:
488*7c657876SArnaldo Carvalho de Melo 	release_sock(sk);
489*7c657876SArnaldo Carvalho de Melo 	return err;
490*7c657876SArnaldo Carvalho de Melo }
491*7c657876SArnaldo Carvalho de Melo 
492*7c657876SArnaldo Carvalho de Melo static const unsigned char dccp_new_state[] = {
493*7c657876SArnaldo Carvalho de Melo 	/* current state:        new state:      action:	*/
494*7c657876SArnaldo Carvalho de Melo 	[0]			= DCCP_CLOSED,
495*7c657876SArnaldo Carvalho de Melo 	[DCCP_OPEN] 		= DCCP_CLOSING | DCCP_ACTION_FIN,
496*7c657876SArnaldo Carvalho de Melo 	[DCCP_REQUESTING] 	= DCCP_CLOSED,
497*7c657876SArnaldo Carvalho de Melo 	[DCCP_PARTOPEN]	= DCCP_CLOSING | DCCP_ACTION_FIN,
498*7c657876SArnaldo Carvalho de Melo 	[DCCP_LISTEN]		= DCCP_CLOSED,
499*7c657876SArnaldo Carvalho de Melo 	[DCCP_RESPOND] 	= DCCP_CLOSED,
500*7c657876SArnaldo Carvalho de Melo 	[DCCP_CLOSING]	= DCCP_CLOSED,
501*7c657876SArnaldo Carvalho de Melo 	[DCCP_TIME_WAIT] 	= DCCP_CLOSED,
502*7c657876SArnaldo Carvalho de Melo 	[DCCP_CLOSED] 	= DCCP_CLOSED,
503*7c657876SArnaldo Carvalho de Melo };
504*7c657876SArnaldo Carvalho de Melo 
505*7c657876SArnaldo Carvalho de Melo static int dccp_close_state(struct sock *sk)
506*7c657876SArnaldo Carvalho de Melo {
507*7c657876SArnaldo Carvalho de Melo 	const int next = dccp_new_state[sk->sk_state];
508*7c657876SArnaldo Carvalho de Melo 	const int ns = next & DCCP_STATE_MASK;
509*7c657876SArnaldo Carvalho de Melo 
510*7c657876SArnaldo Carvalho de Melo 	if (ns != sk->sk_state)
511*7c657876SArnaldo Carvalho de Melo 		dccp_set_state(sk, ns);
512*7c657876SArnaldo Carvalho de Melo 
513*7c657876SArnaldo Carvalho de Melo 	return next & DCCP_ACTION_FIN;
514*7c657876SArnaldo Carvalho de Melo }
515*7c657876SArnaldo Carvalho de Melo 
516*7c657876SArnaldo Carvalho de Melo void dccp_close(struct sock *sk, long timeout)
517*7c657876SArnaldo Carvalho de Melo {
518*7c657876SArnaldo Carvalho de Melo 	struct sk_buff *skb;
519*7c657876SArnaldo Carvalho de Melo 
520*7c657876SArnaldo Carvalho de Melo 	lock_sock(sk);
521*7c657876SArnaldo Carvalho de Melo 
522*7c657876SArnaldo Carvalho de Melo 	sk->sk_shutdown = SHUTDOWN_MASK;
523*7c657876SArnaldo Carvalho de Melo 
524*7c657876SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_LISTEN) {
525*7c657876SArnaldo Carvalho de Melo 		dccp_set_state(sk, DCCP_CLOSED);
526*7c657876SArnaldo Carvalho de Melo 
527*7c657876SArnaldo Carvalho de Melo 		/* Special case. */
528*7c657876SArnaldo Carvalho de Melo 		inet_csk_listen_stop(sk);
529*7c657876SArnaldo Carvalho de Melo 
530*7c657876SArnaldo Carvalho de Melo 		goto adjudge_to_death;
531*7c657876SArnaldo Carvalho de Melo 	}
532*7c657876SArnaldo Carvalho de Melo 
533*7c657876SArnaldo Carvalho de Melo 	/*
534*7c657876SArnaldo Carvalho de Melo 	 * We need to flush the recv. buffs.  We do this only on the
535*7c657876SArnaldo Carvalho de Melo 	 * descriptor close, not protocol-sourced closes, because the
536*7c657876SArnaldo Carvalho de Melo 	  *reader process may not have drained the data yet!
537*7c657876SArnaldo Carvalho de Melo 	 */
538*7c657876SArnaldo Carvalho de Melo 	/* FIXME: check for unread data */
539*7c657876SArnaldo Carvalho de Melo 	while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
540*7c657876SArnaldo Carvalho de Melo 		__kfree_skb(skb);
541*7c657876SArnaldo Carvalho de Melo 	}
542*7c657876SArnaldo Carvalho de Melo 
543*7c657876SArnaldo Carvalho de Melo 	if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
544*7c657876SArnaldo Carvalho de Melo 		/* Check zero linger _after_ checking for unread data. */
545*7c657876SArnaldo Carvalho de Melo 		sk->sk_prot->disconnect(sk, 0);
546*7c657876SArnaldo Carvalho de Melo 	} else if (dccp_close_state(sk)) {
547*7c657876SArnaldo Carvalho de Melo 		dccp_send_close(sk);
548*7c657876SArnaldo Carvalho de Melo 	}
549*7c657876SArnaldo Carvalho de Melo 
550*7c657876SArnaldo Carvalho de Melo 	sk_stream_wait_close(sk, timeout);
551*7c657876SArnaldo Carvalho de Melo 
552*7c657876SArnaldo Carvalho de Melo adjudge_to_death:
553*7c657876SArnaldo Carvalho de Melo 	release_sock(sk);
554*7c657876SArnaldo Carvalho de Melo 	/*
555*7c657876SArnaldo Carvalho de Melo 	 * Now socket is owned by kernel and we acquire BH lock
556*7c657876SArnaldo Carvalho de Melo 	 * to finish close. No need to check for user refs.
557*7c657876SArnaldo Carvalho de Melo 	 */
558*7c657876SArnaldo Carvalho de Melo 	local_bh_disable();
559*7c657876SArnaldo Carvalho de Melo 	bh_lock_sock(sk);
560*7c657876SArnaldo Carvalho de Melo 	BUG_TRAP(!sock_owned_by_user(sk));
561*7c657876SArnaldo Carvalho de Melo 
562*7c657876SArnaldo Carvalho de Melo 	sock_hold(sk);
563*7c657876SArnaldo Carvalho de Melo 	sock_orphan(sk);
564*7c657876SArnaldo Carvalho de Melo 
565*7c657876SArnaldo Carvalho de Melo 	if (sk->sk_state != DCCP_CLOSED)
566*7c657876SArnaldo Carvalho de Melo 		dccp_set_state(sk, DCCP_CLOSED);
567*7c657876SArnaldo Carvalho de Melo 
568*7c657876SArnaldo Carvalho de Melo 	atomic_inc(&dccp_orphan_count);
569*7c657876SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_CLOSED)
570*7c657876SArnaldo Carvalho de Melo 		inet_csk_destroy_sock(sk);
571*7c657876SArnaldo Carvalho de Melo 
572*7c657876SArnaldo Carvalho de Melo 	/* Otherwise, socket is reprieved until protocol close. */
573*7c657876SArnaldo Carvalho de Melo 
574*7c657876SArnaldo Carvalho de Melo 	bh_unlock_sock(sk);
575*7c657876SArnaldo Carvalho de Melo 	local_bh_enable();
576*7c657876SArnaldo Carvalho de Melo 	sock_put(sk);
577*7c657876SArnaldo Carvalho de Melo }
578*7c657876SArnaldo Carvalho de Melo 
579*7c657876SArnaldo Carvalho de Melo void dccp_shutdown(struct sock *sk, int how)
580*7c657876SArnaldo Carvalho de Melo {
581*7c657876SArnaldo Carvalho de Melo 	dccp_pr_debug("entry\n");
582*7c657876SArnaldo Carvalho de Melo }
583*7c657876SArnaldo Carvalho de Melo 
584*7c657876SArnaldo Carvalho de Melo struct proto_ops inet_dccp_ops = {
585*7c657876SArnaldo Carvalho de Melo 	.family		= PF_INET,
586*7c657876SArnaldo Carvalho de Melo 	.owner		= THIS_MODULE,
587*7c657876SArnaldo Carvalho de Melo 	.release	= inet_release,
588*7c657876SArnaldo Carvalho de Melo 	.bind		= inet_bind,
589*7c657876SArnaldo Carvalho de Melo 	.connect	= inet_stream_connect,
590*7c657876SArnaldo Carvalho de Melo 	.socketpair	= sock_no_socketpair,
591*7c657876SArnaldo Carvalho de Melo 	.accept		= inet_accept,
592*7c657876SArnaldo Carvalho de Melo 	.getname	= inet_getname,
593*7c657876SArnaldo Carvalho de Melo 	.poll		= sock_no_poll,
594*7c657876SArnaldo Carvalho de Melo 	.ioctl		= inet_ioctl,
595*7c657876SArnaldo Carvalho de Melo 	.listen		= inet_dccp_listen, /* FIXME: work on inet_listen to rename it to sock_common_listen */
596*7c657876SArnaldo Carvalho de Melo 	.shutdown	= inet_shutdown,
597*7c657876SArnaldo Carvalho de Melo 	.setsockopt	= sock_common_setsockopt,
598*7c657876SArnaldo Carvalho de Melo 	.getsockopt	= sock_common_getsockopt,
599*7c657876SArnaldo Carvalho de Melo 	.sendmsg	= inet_sendmsg,
600*7c657876SArnaldo Carvalho de Melo 	.recvmsg	= sock_common_recvmsg,
601*7c657876SArnaldo Carvalho de Melo 	.mmap		= sock_no_mmap,
602*7c657876SArnaldo Carvalho de Melo 	.sendpage	= sock_no_sendpage,
603*7c657876SArnaldo Carvalho de Melo };
604*7c657876SArnaldo Carvalho de Melo 
605*7c657876SArnaldo Carvalho de Melo extern struct net_proto_family inet_family_ops;
606*7c657876SArnaldo Carvalho de Melo 
607*7c657876SArnaldo Carvalho de Melo static struct inet_protosw dccp_v4_protosw = {
608*7c657876SArnaldo Carvalho de Melo 	.type		= SOCK_DCCP,
609*7c657876SArnaldo Carvalho de Melo 	.protocol	= IPPROTO_DCCP,
610*7c657876SArnaldo Carvalho de Melo 	.prot		= &dccp_v4_prot,
611*7c657876SArnaldo Carvalho de Melo 	.ops		= &inet_dccp_ops,
612*7c657876SArnaldo Carvalho de Melo 	.capability	= -1,
613*7c657876SArnaldo Carvalho de Melo 	.no_check	= 0,
614*7c657876SArnaldo Carvalho de Melo 	.flags		= 0,
615*7c657876SArnaldo Carvalho de Melo };
616*7c657876SArnaldo Carvalho de Melo 
617*7c657876SArnaldo Carvalho de Melo /*
618*7c657876SArnaldo Carvalho de Melo  * This is the global socket data structure used for responding to
619*7c657876SArnaldo Carvalho de Melo  * the Out-of-the-blue (OOTB) packets. A control sock will be created
620*7c657876SArnaldo Carvalho de Melo  * for this socket at the initialization time.
621*7c657876SArnaldo Carvalho de Melo  */
622*7c657876SArnaldo Carvalho de Melo struct socket *dccp_ctl_socket;
623*7c657876SArnaldo Carvalho de Melo 
624*7c657876SArnaldo Carvalho de Melo static char dccp_ctl_socket_err_msg[] __initdata =
625*7c657876SArnaldo Carvalho de Melo 	KERN_ERR "DCCP: Failed to create the control socket.\n";
626*7c657876SArnaldo Carvalho de Melo 
627*7c657876SArnaldo Carvalho de Melo static int __init dccp_ctl_sock_init(void)
628*7c657876SArnaldo Carvalho de Melo {
629*7c657876SArnaldo Carvalho de Melo 	int rc = sock_create_kern(PF_INET, SOCK_DCCP, IPPROTO_DCCP,
630*7c657876SArnaldo Carvalho de Melo 				  &dccp_ctl_socket);
631*7c657876SArnaldo Carvalho de Melo 	if (rc < 0)
632*7c657876SArnaldo Carvalho de Melo 		printk(dccp_ctl_socket_err_msg);
633*7c657876SArnaldo Carvalho de Melo 	else {
634*7c657876SArnaldo Carvalho de Melo 		dccp_ctl_socket->sk->sk_allocation = GFP_ATOMIC;
635*7c657876SArnaldo Carvalho de Melo 		inet_sk(dccp_ctl_socket->sk)->uc_ttl = -1;
636*7c657876SArnaldo Carvalho de Melo 
637*7c657876SArnaldo Carvalho de Melo 		/* Unhash it so that IP input processing does not even
638*7c657876SArnaldo Carvalho de Melo 		 * see it, we do not wish this socket to see incoming
639*7c657876SArnaldo Carvalho de Melo 		 * packets.
640*7c657876SArnaldo Carvalho de Melo 		 */
641*7c657876SArnaldo Carvalho de Melo 		dccp_ctl_socket->sk->sk_prot->unhash(dccp_ctl_socket->sk);
642*7c657876SArnaldo Carvalho de Melo 	}
643*7c657876SArnaldo Carvalho de Melo 
644*7c657876SArnaldo Carvalho de Melo 	return rc;
645*7c657876SArnaldo Carvalho de Melo }
646*7c657876SArnaldo Carvalho de Melo 
647*7c657876SArnaldo Carvalho de Melo static void __exit dccp_ctl_sock_exit(void)
648*7c657876SArnaldo Carvalho de Melo {
649*7c657876SArnaldo Carvalho de Melo 	if (dccp_ctl_socket != NULL)
650*7c657876SArnaldo Carvalho de Melo 		sock_release(dccp_ctl_socket);
651*7c657876SArnaldo Carvalho de Melo }
652*7c657876SArnaldo Carvalho de Melo 
653*7c657876SArnaldo Carvalho de Melo static int __init init_dccp_v4_mibs(void)
654*7c657876SArnaldo Carvalho de Melo {
655*7c657876SArnaldo Carvalho de Melo 	int rc = -ENOMEM;
656*7c657876SArnaldo Carvalho de Melo 
657*7c657876SArnaldo Carvalho de Melo 	dccp_statistics[0] = alloc_percpu(struct dccp_mib);
658*7c657876SArnaldo Carvalho de Melo 	if (dccp_statistics[0] == NULL)
659*7c657876SArnaldo Carvalho de Melo 		goto out;
660*7c657876SArnaldo Carvalho de Melo 
661*7c657876SArnaldo Carvalho de Melo 	dccp_statistics[1] = alloc_percpu(struct dccp_mib);
662*7c657876SArnaldo Carvalho de Melo 	if (dccp_statistics[1] == NULL)
663*7c657876SArnaldo Carvalho de Melo 		goto out_free_one;
664*7c657876SArnaldo Carvalho de Melo 
665*7c657876SArnaldo Carvalho de Melo 	rc = 0;
666*7c657876SArnaldo Carvalho de Melo out:
667*7c657876SArnaldo Carvalho de Melo 	return rc;
668*7c657876SArnaldo Carvalho de Melo out_free_one:
669*7c657876SArnaldo Carvalho de Melo 	free_percpu(dccp_statistics[0]);
670*7c657876SArnaldo Carvalho de Melo 	dccp_statistics[0] = NULL;
671*7c657876SArnaldo Carvalho de Melo 	goto out;
672*7c657876SArnaldo Carvalho de Melo 
673*7c657876SArnaldo Carvalho de Melo }
674*7c657876SArnaldo Carvalho de Melo 
675*7c657876SArnaldo Carvalho de Melo static int thash_entries;
676*7c657876SArnaldo Carvalho de Melo module_param(thash_entries, int, 0444);
677*7c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
678*7c657876SArnaldo Carvalho de Melo 
679*7c657876SArnaldo Carvalho de Melo int dccp_debug;
680*7c657876SArnaldo Carvalho de Melo module_param(dccp_debug, int, 0444);
681*7c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(dccp_debug, "Enable debug messages");
682*7c657876SArnaldo Carvalho de Melo 
683*7c657876SArnaldo Carvalho de Melo static int __init dccp_init(void)
684*7c657876SArnaldo Carvalho de Melo {
685*7c657876SArnaldo Carvalho de Melo 	unsigned long goal;
686*7c657876SArnaldo Carvalho de Melo 	int ehash_order, bhash_order, i;
687*7c657876SArnaldo Carvalho de Melo 	int rc = proto_register(&dccp_v4_prot, 1);
688*7c657876SArnaldo Carvalho de Melo 
689*7c657876SArnaldo Carvalho de Melo 	if (rc)
690*7c657876SArnaldo Carvalho de Melo 		goto out;
691*7c657876SArnaldo Carvalho de Melo 
692*7c657876SArnaldo Carvalho de Melo 	dccp_hashinfo.bind_bucket_cachep = kmem_cache_create("dccp_bind_bucket",
693*7c657876SArnaldo Carvalho de Melo 					       sizeof(struct inet_bind_bucket),
694*7c657876SArnaldo Carvalho de Melo 					       0, SLAB_HWCACHE_ALIGN,
695*7c657876SArnaldo Carvalho de Melo 					       NULL, NULL);
696*7c657876SArnaldo Carvalho de Melo 	if (!dccp_hashinfo.bind_bucket_cachep)
697*7c657876SArnaldo Carvalho de Melo 		goto out_proto_unregister;
698*7c657876SArnaldo Carvalho de Melo 
699*7c657876SArnaldo Carvalho de Melo 	/*
700*7c657876SArnaldo Carvalho de Melo 	 * Size and allocate the main established and bind bucket
701*7c657876SArnaldo Carvalho de Melo 	 * hash tables.
702*7c657876SArnaldo Carvalho de Melo 	 *
703*7c657876SArnaldo Carvalho de Melo 	 * The methodology is similar to that of the buffer cache.
704*7c657876SArnaldo Carvalho de Melo 	 */
705*7c657876SArnaldo Carvalho de Melo 	if (num_physpages >= (128 * 1024))
706*7c657876SArnaldo Carvalho de Melo 		goal = num_physpages >> (21 - PAGE_SHIFT);
707*7c657876SArnaldo Carvalho de Melo 	else
708*7c657876SArnaldo Carvalho de Melo 		goal = num_physpages >> (23 - PAGE_SHIFT);
709*7c657876SArnaldo Carvalho de Melo 
710*7c657876SArnaldo Carvalho de Melo 	if (thash_entries)
711*7c657876SArnaldo Carvalho de Melo 		goal = (thash_entries * sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT;
712*7c657876SArnaldo Carvalho de Melo 	for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++)
713*7c657876SArnaldo Carvalho de Melo 		;
714*7c657876SArnaldo Carvalho de Melo 	do {
715*7c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE /
716*7c657876SArnaldo Carvalho de Melo 					sizeof(struct inet_ehash_bucket);
717*7c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.ehash_size >>= 1;
718*7c657876SArnaldo Carvalho de Melo 		while (dccp_hashinfo.ehash_size & (dccp_hashinfo.ehash_size - 1))
719*7c657876SArnaldo Carvalho de Melo 			dccp_hashinfo.ehash_size--;
720*7c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.ehash = (struct inet_ehash_bucket *)
721*7c657876SArnaldo Carvalho de Melo 			__get_free_pages(GFP_ATOMIC, ehash_order);
722*7c657876SArnaldo Carvalho de Melo 	} while (!dccp_hashinfo.ehash && --ehash_order > 0);
723*7c657876SArnaldo Carvalho de Melo 
724*7c657876SArnaldo Carvalho de Melo 	if (!dccp_hashinfo.ehash) {
725*7c657876SArnaldo Carvalho de Melo 		printk(KERN_CRIT "Failed to allocate DCCP "
726*7c657876SArnaldo Carvalho de Melo 				 "established hash table\n");
727*7c657876SArnaldo Carvalho de Melo 		goto out_free_bind_bucket_cachep;
728*7c657876SArnaldo Carvalho de Melo 	}
729*7c657876SArnaldo Carvalho de Melo 
730*7c657876SArnaldo Carvalho de Melo 	for (i = 0; i < (dccp_hashinfo.ehash_size << 1); i++) {
731*7c657876SArnaldo Carvalho de Melo 		rwlock_init(&dccp_hashinfo.ehash[i].lock);
732*7c657876SArnaldo Carvalho de Melo 		INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain);
733*7c657876SArnaldo Carvalho de Melo 	}
734*7c657876SArnaldo Carvalho de Melo 
735*7c657876SArnaldo Carvalho de Melo 	bhash_order = ehash_order;
736*7c657876SArnaldo Carvalho de Melo 
737*7c657876SArnaldo Carvalho de Melo 	do {
738*7c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE /
739*7c657876SArnaldo Carvalho de Melo 					sizeof(struct inet_bind_hashbucket);
740*7c657876SArnaldo Carvalho de Melo 		if ((dccp_hashinfo.bhash_size > (64 * 1024)) && bhash_order > 0)
741*7c657876SArnaldo Carvalho de Melo 			continue;
742*7c657876SArnaldo Carvalho de Melo 		dccp_hashinfo.bhash = (struct inet_bind_hashbucket *)
743*7c657876SArnaldo Carvalho de Melo 			__get_free_pages(GFP_ATOMIC, bhash_order);
744*7c657876SArnaldo Carvalho de Melo 	} while (!dccp_hashinfo.bhash && --bhash_order >= 0);
745*7c657876SArnaldo Carvalho de Melo 
746*7c657876SArnaldo Carvalho de Melo 	if (!dccp_hashinfo.bhash) {
747*7c657876SArnaldo Carvalho de Melo 		printk(KERN_CRIT "Failed to allocate DCCP bind hash table\n");
748*7c657876SArnaldo Carvalho de Melo 		goto out_free_dccp_ehash;
749*7c657876SArnaldo Carvalho de Melo 	}
750*7c657876SArnaldo Carvalho de Melo 
751*7c657876SArnaldo Carvalho de Melo 	for (i = 0; i < dccp_hashinfo.bhash_size; i++) {
752*7c657876SArnaldo Carvalho de Melo 		spin_lock_init(&dccp_hashinfo.bhash[i].lock);
753*7c657876SArnaldo Carvalho de Melo 		INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain);
754*7c657876SArnaldo Carvalho de Melo 	}
755*7c657876SArnaldo Carvalho de Melo 
756*7c657876SArnaldo Carvalho de Melo 	if (init_dccp_v4_mibs())
757*7c657876SArnaldo Carvalho de Melo 		goto out_free_dccp_bhash;
758*7c657876SArnaldo Carvalho de Melo 
759*7c657876SArnaldo Carvalho de Melo 	rc = -EAGAIN;
760*7c657876SArnaldo Carvalho de Melo 	if (inet_add_protocol(&dccp_protocol, IPPROTO_DCCP))
761*7c657876SArnaldo Carvalho de Melo 		goto out_free_dccp_v4_mibs;
762*7c657876SArnaldo Carvalho de Melo 
763*7c657876SArnaldo Carvalho de Melo 	inet_register_protosw(&dccp_v4_protosw);
764*7c657876SArnaldo Carvalho de Melo 
765*7c657876SArnaldo Carvalho de Melo 	rc = dccp_ctl_sock_init();
766*7c657876SArnaldo Carvalho de Melo 	if (rc)
767*7c657876SArnaldo Carvalho de Melo 		goto out_unregister_protosw;
768*7c657876SArnaldo Carvalho de Melo out:
769*7c657876SArnaldo Carvalho de Melo 	return rc;
770*7c657876SArnaldo Carvalho de Melo out_unregister_protosw:
771*7c657876SArnaldo Carvalho de Melo 	inet_unregister_protosw(&dccp_v4_protosw);
772*7c657876SArnaldo Carvalho de Melo 	inet_del_protocol(&dccp_protocol, IPPROTO_DCCP);
773*7c657876SArnaldo Carvalho de Melo out_free_dccp_v4_mibs:
774*7c657876SArnaldo Carvalho de Melo 	free_percpu(dccp_statistics[0]);
775*7c657876SArnaldo Carvalho de Melo 	free_percpu(dccp_statistics[1]);
776*7c657876SArnaldo Carvalho de Melo 	dccp_statistics[0] = dccp_statistics[1] = NULL;
777*7c657876SArnaldo Carvalho de Melo out_free_dccp_bhash:
778*7c657876SArnaldo Carvalho de Melo 	free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
779*7c657876SArnaldo Carvalho de Melo 	dccp_hashinfo.bhash = NULL;
780*7c657876SArnaldo Carvalho de Melo out_free_dccp_ehash:
781*7c657876SArnaldo Carvalho de Melo 	free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order);
782*7c657876SArnaldo Carvalho de Melo 	dccp_hashinfo.ehash = NULL;
783*7c657876SArnaldo Carvalho de Melo out_free_bind_bucket_cachep:
784*7c657876SArnaldo Carvalho de Melo 	kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
785*7c657876SArnaldo Carvalho de Melo 	dccp_hashinfo.bind_bucket_cachep = NULL;
786*7c657876SArnaldo Carvalho de Melo out_proto_unregister:
787*7c657876SArnaldo Carvalho de Melo 	proto_unregister(&dccp_v4_prot);
788*7c657876SArnaldo Carvalho de Melo 	goto out;
789*7c657876SArnaldo Carvalho de Melo }
790*7c657876SArnaldo Carvalho de Melo 
791*7c657876SArnaldo Carvalho de Melo static const char dccp_del_proto_err_msg[] __exitdata =
792*7c657876SArnaldo Carvalho de Melo 	KERN_ERR "can't remove dccp net_protocol\n";
793*7c657876SArnaldo Carvalho de Melo 
794*7c657876SArnaldo Carvalho de Melo static void __exit dccp_fini(void)
795*7c657876SArnaldo Carvalho de Melo {
796*7c657876SArnaldo Carvalho de Melo 	dccp_ctl_sock_exit();
797*7c657876SArnaldo Carvalho de Melo 
798*7c657876SArnaldo Carvalho de Melo 	inet_unregister_protosw(&dccp_v4_protosw);
799*7c657876SArnaldo Carvalho de Melo 
800*7c657876SArnaldo Carvalho de Melo 	if (inet_del_protocol(&dccp_protocol, IPPROTO_DCCP) < 0)
801*7c657876SArnaldo Carvalho de Melo 		printk(dccp_del_proto_err_msg);
802*7c657876SArnaldo Carvalho de Melo 
803*7c657876SArnaldo Carvalho de Melo 	/* Free the control endpoint.  */
804*7c657876SArnaldo Carvalho de Melo 	sock_release(dccp_ctl_socket);
805*7c657876SArnaldo Carvalho de Melo 
806*7c657876SArnaldo Carvalho de Melo 	proto_unregister(&dccp_v4_prot);
807*7c657876SArnaldo Carvalho de Melo 
808*7c657876SArnaldo Carvalho de Melo 	kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
809*7c657876SArnaldo Carvalho de Melo }
810*7c657876SArnaldo Carvalho de Melo 
811*7c657876SArnaldo Carvalho de Melo module_init(dccp_init);
812*7c657876SArnaldo Carvalho de Melo module_exit(dccp_fini);
813*7c657876SArnaldo Carvalho de Melo 
814*7c657876SArnaldo Carvalho de Melo /* __stringify doesn't likes enums, so use SOCK_DCCP (6) value directly  */
815*7c657876SArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-6");
816*7c657876SArnaldo Carvalho de Melo MODULE_LICENSE("GPL");
817*7c657876SArnaldo Carvalho de Melo MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>");
818*7c657876SArnaldo Carvalho de Melo MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol");
819