162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  net/dccp/proto.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  An implementation of the DCCP protocol
662306a36Sopenharmony_ci *  Arnaldo Carvalho de Melo <acme@conectiva.com.br>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/dccp.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/sched.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/skbuff.h>
1562306a36Sopenharmony_ci#include <linux/netdevice.h>
1662306a36Sopenharmony_ci#include <linux/in.h>
1762306a36Sopenharmony_ci#include <linux/if_arp.h>
1862306a36Sopenharmony_ci#include <linux/init.h>
1962306a36Sopenharmony_ci#include <linux/random.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <net/checksum.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <net/inet_sock.h>
2462306a36Sopenharmony_ci#include <net/inet_common.h>
2562306a36Sopenharmony_ci#include <net/sock.h>
2662306a36Sopenharmony_ci#include <net/xfrm.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <asm/ioctls.h>
2962306a36Sopenharmony_ci#include <linux/spinlock.h>
3062306a36Sopenharmony_ci#include <linux/timer.h>
3162306a36Sopenharmony_ci#include <linux/delay.h>
3262306a36Sopenharmony_ci#include <linux/poll.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include "ccid.h"
3562306a36Sopenharmony_ci#include "dccp.h"
3662306a36Sopenharmony_ci#include "feat.h"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
3962306a36Sopenharmony_ci#include "trace.h"
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ciDEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_statistics);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciDEFINE_PER_CPU(unsigned int, dccp_orphan_count);
4662306a36Sopenharmony_ciEXPORT_PER_CPU_SYMBOL_GPL(dccp_orphan_count);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct inet_hashinfo dccp_hashinfo;
4962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_hashinfo);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* the maximum queue length for tx in packets. 0 is no limit */
5262306a36Sopenharmony_ciint sysctl_dccp_tx_qlen __read_mostly = 5;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#ifdef CONFIG_IP_DCCP_DEBUG
5562306a36Sopenharmony_cistatic const char *dccp_state_name(const int state)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	static const char *const dccp_state_names[] = {
5862306a36Sopenharmony_ci	[DCCP_OPEN]		= "OPEN",
5962306a36Sopenharmony_ci	[DCCP_REQUESTING]	= "REQUESTING",
6062306a36Sopenharmony_ci	[DCCP_PARTOPEN]		= "PARTOPEN",
6162306a36Sopenharmony_ci	[DCCP_LISTEN]		= "LISTEN",
6262306a36Sopenharmony_ci	[DCCP_RESPOND]		= "RESPOND",
6362306a36Sopenharmony_ci	[DCCP_CLOSING]		= "CLOSING",
6462306a36Sopenharmony_ci	[DCCP_ACTIVE_CLOSEREQ]	= "CLOSEREQ",
6562306a36Sopenharmony_ci	[DCCP_PASSIVE_CLOSE]	= "PASSIVE_CLOSE",
6662306a36Sopenharmony_ci	[DCCP_PASSIVE_CLOSEREQ]	= "PASSIVE_CLOSEREQ",
6762306a36Sopenharmony_ci	[DCCP_TIME_WAIT]	= "TIME_WAIT",
6862306a36Sopenharmony_ci	[DCCP_CLOSED]		= "CLOSED",
6962306a36Sopenharmony_ci	};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (state >= DCCP_MAX_STATES)
7262306a36Sopenharmony_ci		return "INVALID STATE!";
7362306a36Sopenharmony_ci	else
7462306a36Sopenharmony_ci		return dccp_state_names[state];
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci#endif
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_civoid dccp_set_state(struct sock *sk, const int state)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	const int oldstate = sk->sk_state;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	dccp_pr_debug("%s(%p)  %s  -->  %s\n", dccp_role(sk), sk,
8362306a36Sopenharmony_ci		      dccp_state_name(oldstate), dccp_state_name(state));
8462306a36Sopenharmony_ci	WARN_ON(state == oldstate);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	switch (state) {
8762306a36Sopenharmony_ci	case DCCP_OPEN:
8862306a36Sopenharmony_ci		if (oldstate != DCCP_OPEN)
8962306a36Sopenharmony_ci			DCCP_INC_STATS(DCCP_MIB_CURRESTAB);
9062306a36Sopenharmony_ci		/* Client retransmits all Confirm options until entering OPEN */
9162306a36Sopenharmony_ci		if (oldstate == DCCP_PARTOPEN)
9262306a36Sopenharmony_ci			dccp_feat_list_purge(&dccp_sk(sk)->dccps_featneg);
9362306a36Sopenharmony_ci		break;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	case DCCP_CLOSED:
9662306a36Sopenharmony_ci		if (oldstate == DCCP_OPEN || oldstate == DCCP_ACTIVE_CLOSEREQ ||
9762306a36Sopenharmony_ci		    oldstate == DCCP_CLOSING)
9862306a36Sopenharmony_ci			DCCP_INC_STATS(DCCP_MIB_ESTABRESETS);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		sk->sk_prot->unhash(sk);
10162306a36Sopenharmony_ci		if (inet_csk(sk)->icsk_bind_hash != NULL &&
10262306a36Sopenharmony_ci		    !(sk->sk_userlocks & SOCK_BINDPORT_LOCK))
10362306a36Sopenharmony_ci			inet_put_port(sk);
10462306a36Sopenharmony_ci		fallthrough;
10562306a36Sopenharmony_ci	default:
10662306a36Sopenharmony_ci		if (oldstate == DCCP_OPEN)
10762306a36Sopenharmony_ci			DCCP_DEC_STATS(DCCP_MIB_CURRESTAB);
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* Change state AFTER socket is unhashed to avoid closed
11162306a36Sopenharmony_ci	 * socket sitting in hash tables.
11262306a36Sopenharmony_ci	 */
11362306a36Sopenharmony_ci	inet_sk_set_state(sk, state);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_set_state);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void dccp_finish_passive_close(struct sock *sk)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	switch (sk->sk_state) {
12162306a36Sopenharmony_ci	case DCCP_PASSIVE_CLOSE:
12262306a36Sopenharmony_ci		/* Node (client or server) has received Close packet. */
12362306a36Sopenharmony_ci		dccp_send_reset(sk, DCCP_RESET_CODE_CLOSED);
12462306a36Sopenharmony_ci		dccp_set_state(sk, DCCP_CLOSED);
12562306a36Sopenharmony_ci		break;
12662306a36Sopenharmony_ci	case DCCP_PASSIVE_CLOSEREQ:
12762306a36Sopenharmony_ci		/*
12862306a36Sopenharmony_ci		 * Client received CloseReq. We set the `active' flag so that
12962306a36Sopenharmony_ci		 * dccp_send_close() retransmits the Close as per RFC 4340, 8.3.
13062306a36Sopenharmony_ci		 */
13162306a36Sopenharmony_ci		dccp_send_close(sk, 1);
13262306a36Sopenharmony_ci		dccp_set_state(sk, DCCP_CLOSING);
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_civoid dccp_done(struct sock *sk)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	dccp_set_state(sk, DCCP_CLOSED);
13962306a36Sopenharmony_ci	dccp_clear_xmit_timers(sk);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	sk->sk_shutdown = SHUTDOWN_MASK;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (!sock_flag(sk, SOCK_DEAD))
14462306a36Sopenharmony_ci		sk->sk_state_change(sk);
14562306a36Sopenharmony_ci	else
14662306a36Sopenharmony_ci		inet_csk_destroy_sock(sk);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_done);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ciconst char *dccp_packet_name(const int type)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	static const char *const dccp_packet_names[] = {
15462306a36Sopenharmony_ci		[DCCP_PKT_REQUEST]  = "REQUEST",
15562306a36Sopenharmony_ci		[DCCP_PKT_RESPONSE] = "RESPONSE",
15662306a36Sopenharmony_ci		[DCCP_PKT_DATA]	    = "DATA",
15762306a36Sopenharmony_ci		[DCCP_PKT_ACK]	    = "ACK",
15862306a36Sopenharmony_ci		[DCCP_PKT_DATAACK]  = "DATAACK",
15962306a36Sopenharmony_ci		[DCCP_PKT_CLOSEREQ] = "CLOSEREQ",
16062306a36Sopenharmony_ci		[DCCP_PKT_CLOSE]    = "CLOSE",
16162306a36Sopenharmony_ci		[DCCP_PKT_RESET]    = "RESET",
16262306a36Sopenharmony_ci		[DCCP_PKT_SYNC]	    = "SYNC",
16362306a36Sopenharmony_ci		[DCCP_PKT_SYNCACK]  = "SYNCACK",
16462306a36Sopenharmony_ci	};
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (type >= DCCP_NR_PKT_TYPES)
16762306a36Sopenharmony_ci		return "INVALID";
16862306a36Sopenharmony_ci	else
16962306a36Sopenharmony_ci		return dccp_packet_names[type];
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_packet_name);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_civoid dccp_destruct_common(struct sock *sk)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct dccp_sock *dp = dccp_sk(sk);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
17962306a36Sopenharmony_ci	dp->dccps_hc_tx_ccid = NULL;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_destruct_common);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic void dccp_sk_destruct(struct sock *sk)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	dccp_destruct_common(sk);
18662306a36Sopenharmony_ci	inet_sock_destruct(sk);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ciint dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct dccp_sock *dp = dccp_sk(sk);
19262306a36Sopenharmony_ci	struct inet_connection_sock *icsk = inet_csk(sk);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	pr_warn_once("DCCP is deprecated and scheduled to be removed in 2025, "
19562306a36Sopenharmony_ci		     "please contact the netdev mailing list\n");
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	icsk->icsk_rto		= DCCP_TIMEOUT_INIT;
19862306a36Sopenharmony_ci	icsk->icsk_syn_retries	= sysctl_dccp_request_retries;
19962306a36Sopenharmony_ci	sk->sk_state		= DCCP_CLOSED;
20062306a36Sopenharmony_ci	sk->sk_write_space	= dccp_write_space;
20162306a36Sopenharmony_ci	sk->sk_destruct		= dccp_sk_destruct;
20262306a36Sopenharmony_ci	icsk->icsk_sync_mss	= dccp_sync_mss;
20362306a36Sopenharmony_ci	dp->dccps_mss_cache	= 536;
20462306a36Sopenharmony_ci	dp->dccps_rate_last	= jiffies;
20562306a36Sopenharmony_ci	dp->dccps_role		= DCCP_ROLE_UNDEFINED;
20662306a36Sopenharmony_ci	dp->dccps_service	= DCCP_SERVICE_CODE_IS_ABSENT;
20762306a36Sopenharmony_ci	dp->dccps_tx_qlen	= sysctl_dccp_tx_qlen;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	dccp_init_xmit_timers(sk);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	INIT_LIST_HEAD(&dp->dccps_featneg);
21262306a36Sopenharmony_ci	/* control socket doesn't need feat nego */
21362306a36Sopenharmony_ci	if (likely(ctl_sock_initialized))
21462306a36Sopenharmony_ci		return dccp_feat_init(sk);
21562306a36Sopenharmony_ci	return 0;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_init_sock);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_civoid dccp_destroy_sock(struct sock *sk)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	struct dccp_sock *dp = dccp_sk(sk);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	__skb_queue_purge(&sk->sk_write_queue);
22562306a36Sopenharmony_ci	if (sk->sk_send_head != NULL) {
22662306a36Sopenharmony_ci		kfree_skb(sk->sk_send_head);
22762306a36Sopenharmony_ci		sk->sk_send_head = NULL;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	/* Clean up a referenced DCCP bind bucket. */
23162306a36Sopenharmony_ci	if (inet_csk(sk)->icsk_bind_hash != NULL)
23262306a36Sopenharmony_ci		inet_put_port(sk);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	kfree(dp->dccps_service_list);
23562306a36Sopenharmony_ci	dp->dccps_service_list = NULL;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (dp->dccps_hc_rx_ackvec != NULL) {
23862306a36Sopenharmony_ci		dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
23962306a36Sopenharmony_ci		dp->dccps_hc_rx_ackvec = NULL;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci	ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
24262306a36Sopenharmony_ci	dp->dccps_hc_rx_ccid = NULL;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* clean up feature negotiation state */
24562306a36Sopenharmony_ci	dccp_feat_list_purge(&dp->dccps_featneg);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_destroy_sock);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic inline int dccp_need_reset(int state)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	return state != DCCP_CLOSED && state != DCCP_LISTEN &&
25362306a36Sopenharmony_ci	       state != DCCP_REQUESTING;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ciint dccp_disconnect(struct sock *sk, int flags)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct inet_connection_sock *icsk = inet_csk(sk);
25962306a36Sopenharmony_ci	struct inet_sock *inet = inet_sk(sk);
26062306a36Sopenharmony_ci	struct dccp_sock *dp = dccp_sk(sk);
26162306a36Sopenharmony_ci	const int old_state = sk->sk_state;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (old_state != DCCP_CLOSED)
26462306a36Sopenharmony_ci		dccp_set_state(sk, DCCP_CLOSED);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/*
26762306a36Sopenharmony_ci	 * This corresponds to the ABORT function of RFC793, sec. 3.8
26862306a36Sopenharmony_ci	 * TCP uses a RST segment, DCCP a Reset packet with Code 2, "Aborted".
26962306a36Sopenharmony_ci	 */
27062306a36Sopenharmony_ci	if (old_state == DCCP_LISTEN) {
27162306a36Sopenharmony_ci		inet_csk_listen_stop(sk);
27262306a36Sopenharmony_ci	} else if (dccp_need_reset(old_state)) {
27362306a36Sopenharmony_ci		dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
27462306a36Sopenharmony_ci		sk->sk_err = ECONNRESET;
27562306a36Sopenharmony_ci	} else if (old_state == DCCP_REQUESTING)
27662306a36Sopenharmony_ci		sk->sk_err = ECONNRESET;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	dccp_clear_xmit_timers(sk);
27962306a36Sopenharmony_ci	ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
28062306a36Sopenharmony_ci	dp->dccps_hc_rx_ccid = NULL;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	__skb_queue_purge(&sk->sk_receive_queue);
28362306a36Sopenharmony_ci	__skb_queue_purge(&sk->sk_write_queue);
28462306a36Sopenharmony_ci	if (sk->sk_send_head != NULL) {
28562306a36Sopenharmony_ci		__kfree_skb(sk->sk_send_head);
28662306a36Sopenharmony_ci		sk->sk_send_head = NULL;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	inet->inet_dport = 0;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	inet_bhash2_reset_saddr(sk);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	sk->sk_shutdown = 0;
29462306a36Sopenharmony_ci	sock_reset_flag(sk, SOCK_DONE);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	icsk->icsk_backoff = 0;
29762306a36Sopenharmony_ci	inet_csk_delack_init(sk);
29862306a36Sopenharmony_ci	__sk_dst_reset(sk);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	WARN_ON(inet->inet_num && !icsk->icsk_bind_hash);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	sk_error_report(sk);
30362306a36Sopenharmony_ci	return 0;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_disconnect);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci/*
30962306a36Sopenharmony_ci *	Wait for a DCCP event.
31062306a36Sopenharmony_ci *
31162306a36Sopenharmony_ci *	Note that we don't need to lock the socket, as the upper poll layers
31262306a36Sopenharmony_ci *	take care of normal races (between the test and the event) and we don't
31362306a36Sopenharmony_ci *	go look at any of the socket buffers directly.
31462306a36Sopenharmony_ci */
31562306a36Sopenharmony_ci__poll_t dccp_poll(struct file *file, struct socket *sock,
31662306a36Sopenharmony_ci		       poll_table *wait)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct sock *sk = sock->sk;
31962306a36Sopenharmony_ci	__poll_t mask;
32062306a36Sopenharmony_ci	u8 shutdown;
32162306a36Sopenharmony_ci	int state;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	sock_poll_wait(file, sock, wait);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	state = inet_sk_state_load(sk);
32662306a36Sopenharmony_ci	if (state == DCCP_LISTEN)
32762306a36Sopenharmony_ci		return inet_csk_listen_poll(sk);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/* Socket is not locked. We are protected from async events
33062306a36Sopenharmony_ci	   by poll logic and correct handling of state changes
33162306a36Sopenharmony_ci	   made by another threads is impossible in any case.
33262306a36Sopenharmony_ci	 */
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	mask = 0;
33562306a36Sopenharmony_ci	if (READ_ONCE(sk->sk_err))
33662306a36Sopenharmony_ci		mask = EPOLLERR;
33762306a36Sopenharmony_ci	shutdown = READ_ONCE(sk->sk_shutdown);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (shutdown == SHUTDOWN_MASK || state == DCCP_CLOSED)
34062306a36Sopenharmony_ci		mask |= EPOLLHUP;
34162306a36Sopenharmony_ci	if (shutdown & RCV_SHUTDOWN)
34262306a36Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/* Connected? */
34562306a36Sopenharmony_ci	if ((1 << state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) {
34662306a36Sopenharmony_ci		if (atomic_read(&sk->sk_rmem_alloc) > 0)
34762306a36Sopenharmony_ci			mask |= EPOLLIN | EPOLLRDNORM;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		if (!(shutdown & SEND_SHUTDOWN)) {
35062306a36Sopenharmony_ci			if (sk_stream_is_writeable(sk)) {
35162306a36Sopenharmony_ci				mask |= EPOLLOUT | EPOLLWRNORM;
35262306a36Sopenharmony_ci			} else {  /* send SIGIO later */
35362306a36Sopenharmony_ci				sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
35462306a36Sopenharmony_ci				set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci				/* Race breaker. If space is freed after
35762306a36Sopenharmony_ci				 * wspace test but before the flags are set,
35862306a36Sopenharmony_ci				 * IO signal will be lost.
35962306a36Sopenharmony_ci				 */
36062306a36Sopenharmony_ci				if (sk_stream_is_writeable(sk))
36162306a36Sopenharmony_ci					mask |= EPOLLOUT | EPOLLWRNORM;
36262306a36Sopenharmony_ci			}
36362306a36Sopenharmony_ci		}
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci	return mask;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_poll);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ciint dccp_ioctl(struct sock *sk, int cmd, int *karg)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	int rc = -ENOTCONN;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	lock_sock(sk);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (sk->sk_state == DCCP_LISTEN)
37662306a36Sopenharmony_ci		goto out;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	switch (cmd) {
37962306a36Sopenharmony_ci	case SIOCOUTQ: {
38062306a36Sopenharmony_ci		*karg = sk_wmem_alloc_get(sk);
38162306a36Sopenharmony_ci		/* Using sk_wmem_alloc here because sk_wmem_queued is not used by DCCP and
38262306a36Sopenharmony_ci		 * always 0, comparably to UDP.
38362306a36Sopenharmony_ci		 */
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		rc = 0;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci		break;
38862306a36Sopenharmony_ci	case SIOCINQ: {
38962306a36Sopenharmony_ci		struct sk_buff *skb;
39062306a36Sopenharmony_ci		*karg = 0;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		skb = skb_peek(&sk->sk_receive_queue);
39362306a36Sopenharmony_ci		if (skb != NULL) {
39462306a36Sopenharmony_ci			/*
39562306a36Sopenharmony_ci			 * We will only return the amount of this packet since
39662306a36Sopenharmony_ci			 * that is all that will be read.
39762306a36Sopenharmony_ci			 */
39862306a36Sopenharmony_ci			*karg = skb->len;
39962306a36Sopenharmony_ci		}
40062306a36Sopenharmony_ci		rc = 0;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci		break;
40362306a36Sopenharmony_ci	default:
40462306a36Sopenharmony_ci		rc = -ENOIOCTLCMD;
40562306a36Sopenharmony_ci		break;
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ciout:
40862306a36Sopenharmony_ci	release_sock(sk);
40962306a36Sopenharmony_ci	return rc;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_ioctl);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic int dccp_setsockopt_service(struct sock *sk, const __be32 service,
41562306a36Sopenharmony_ci				   sockptr_t optval, unsigned int optlen)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct dccp_sock *dp = dccp_sk(sk);
41862306a36Sopenharmony_ci	struct dccp_service_list *sl = NULL;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (service == DCCP_SERVICE_INVALID_VALUE ||
42162306a36Sopenharmony_ci	    optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32))
42262306a36Sopenharmony_ci		return -EINVAL;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (optlen > sizeof(service)) {
42562306a36Sopenharmony_ci		sl = kmalloc(optlen, GFP_KERNEL);
42662306a36Sopenharmony_ci		if (sl == NULL)
42762306a36Sopenharmony_ci			return -ENOMEM;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		sl->dccpsl_nr = optlen / sizeof(u32) - 1;
43062306a36Sopenharmony_ci		if (copy_from_sockptr_offset(sl->dccpsl_list, optval,
43162306a36Sopenharmony_ci				sizeof(service), optlen - sizeof(service)) ||
43262306a36Sopenharmony_ci		    dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) {
43362306a36Sopenharmony_ci			kfree(sl);
43462306a36Sopenharmony_ci			return -EFAULT;
43562306a36Sopenharmony_ci		}
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	lock_sock(sk);
43962306a36Sopenharmony_ci	dp->dccps_service = service;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	kfree(dp->dccps_service_list);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	dp->dccps_service_list = sl;
44462306a36Sopenharmony_ci	release_sock(sk);
44562306a36Sopenharmony_ci	return 0;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic int dccp_setsockopt_cscov(struct sock *sk, int cscov, bool rx)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	u8 *list, len;
45162306a36Sopenharmony_ci	int i, rc;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (cscov < 0 || cscov > 15)
45462306a36Sopenharmony_ci		return -EINVAL;
45562306a36Sopenharmony_ci	/*
45662306a36Sopenharmony_ci	 * Populate a list of permissible values, in the range cscov...15. This
45762306a36Sopenharmony_ci	 * is necessary since feature negotiation of single values only works if
45862306a36Sopenharmony_ci	 * both sides incidentally choose the same value. Since the list starts
45962306a36Sopenharmony_ci	 * lowest-value first, negotiation will pick the smallest shared value.
46062306a36Sopenharmony_ci	 */
46162306a36Sopenharmony_ci	if (cscov == 0)
46262306a36Sopenharmony_ci		return 0;
46362306a36Sopenharmony_ci	len = 16 - cscov;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	list = kmalloc(len, GFP_KERNEL);
46662306a36Sopenharmony_ci	if (list == NULL)
46762306a36Sopenharmony_ci		return -ENOBUFS;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	for (i = 0; i < len; i++)
47062306a36Sopenharmony_ci		list[i] = cscov++;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	rc = dccp_feat_register_sp(sk, DCCPF_MIN_CSUM_COVER, rx, list, len);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (rc == 0) {
47562306a36Sopenharmony_ci		if (rx)
47662306a36Sopenharmony_ci			dccp_sk(sk)->dccps_pcrlen = cscov;
47762306a36Sopenharmony_ci		else
47862306a36Sopenharmony_ci			dccp_sk(sk)->dccps_pcslen = cscov;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci	kfree(list);
48162306a36Sopenharmony_ci	return rc;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int dccp_setsockopt_ccid(struct sock *sk, int type,
48562306a36Sopenharmony_ci				sockptr_t optval, unsigned int optlen)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	u8 *val;
48862306a36Sopenharmony_ci	int rc = 0;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS)
49162306a36Sopenharmony_ci		return -EINVAL;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	val = memdup_sockptr(optval, optlen);
49462306a36Sopenharmony_ci	if (IS_ERR(val))
49562306a36Sopenharmony_ci		return PTR_ERR(val);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	lock_sock(sk);
49862306a36Sopenharmony_ci	if (type == DCCP_SOCKOPT_TX_CCID || type == DCCP_SOCKOPT_CCID)
49962306a36Sopenharmony_ci		rc = dccp_feat_register_sp(sk, DCCPF_CCID, 1, val, optlen);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (!rc && (type == DCCP_SOCKOPT_RX_CCID || type == DCCP_SOCKOPT_CCID))
50262306a36Sopenharmony_ci		rc = dccp_feat_register_sp(sk, DCCPF_CCID, 0, val, optlen);
50362306a36Sopenharmony_ci	release_sock(sk);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	kfree(val);
50662306a36Sopenharmony_ci	return rc;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic int do_dccp_setsockopt(struct sock *sk, int level, int optname,
51062306a36Sopenharmony_ci		sockptr_t optval, unsigned int optlen)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct dccp_sock *dp = dccp_sk(sk);
51362306a36Sopenharmony_ci	int val, err = 0;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	switch (optname) {
51662306a36Sopenharmony_ci	case DCCP_SOCKOPT_PACKET_SIZE:
51762306a36Sopenharmony_ci		DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
51862306a36Sopenharmony_ci		return 0;
51962306a36Sopenharmony_ci	case DCCP_SOCKOPT_CHANGE_L:
52062306a36Sopenharmony_ci	case DCCP_SOCKOPT_CHANGE_R:
52162306a36Sopenharmony_ci		DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n");
52262306a36Sopenharmony_ci		return 0;
52362306a36Sopenharmony_ci	case DCCP_SOCKOPT_CCID:
52462306a36Sopenharmony_ci	case DCCP_SOCKOPT_RX_CCID:
52562306a36Sopenharmony_ci	case DCCP_SOCKOPT_TX_CCID:
52662306a36Sopenharmony_ci		return dccp_setsockopt_ccid(sk, optname, optval, optlen);
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (optlen < (int)sizeof(int))
53062306a36Sopenharmony_ci		return -EINVAL;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (copy_from_sockptr(&val, optval, sizeof(int)))
53362306a36Sopenharmony_ci		return -EFAULT;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (optname == DCCP_SOCKOPT_SERVICE)
53662306a36Sopenharmony_ci		return dccp_setsockopt_service(sk, val, optval, optlen);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	lock_sock(sk);
53962306a36Sopenharmony_ci	switch (optname) {
54062306a36Sopenharmony_ci	case DCCP_SOCKOPT_SERVER_TIMEWAIT:
54162306a36Sopenharmony_ci		if (dp->dccps_role != DCCP_ROLE_SERVER)
54262306a36Sopenharmony_ci			err = -EOPNOTSUPP;
54362306a36Sopenharmony_ci		else
54462306a36Sopenharmony_ci			dp->dccps_server_timewait = (val != 0);
54562306a36Sopenharmony_ci		break;
54662306a36Sopenharmony_ci	case DCCP_SOCKOPT_SEND_CSCOV:
54762306a36Sopenharmony_ci		err = dccp_setsockopt_cscov(sk, val, false);
54862306a36Sopenharmony_ci		break;
54962306a36Sopenharmony_ci	case DCCP_SOCKOPT_RECV_CSCOV:
55062306a36Sopenharmony_ci		err = dccp_setsockopt_cscov(sk, val, true);
55162306a36Sopenharmony_ci		break;
55262306a36Sopenharmony_ci	case DCCP_SOCKOPT_QPOLICY_ID:
55362306a36Sopenharmony_ci		if (sk->sk_state != DCCP_CLOSED)
55462306a36Sopenharmony_ci			err = -EISCONN;
55562306a36Sopenharmony_ci		else if (val < 0 || val >= DCCPQ_POLICY_MAX)
55662306a36Sopenharmony_ci			err = -EINVAL;
55762306a36Sopenharmony_ci		else
55862306a36Sopenharmony_ci			dp->dccps_qpolicy = val;
55962306a36Sopenharmony_ci		break;
56062306a36Sopenharmony_ci	case DCCP_SOCKOPT_QPOLICY_TXQLEN:
56162306a36Sopenharmony_ci		if (val < 0)
56262306a36Sopenharmony_ci			err = -EINVAL;
56362306a36Sopenharmony_ci		else
56462306a36Sopenharmony_ci			dp->dccps_tx_qlen = val;
56562306a36Sopenharmony_ci		break;
56662306a36Sopenharmony_ci	default:
56762306a36Sopenharmony_ci		err = -ENOPROTOOPT;
56862306a36Sopenharmony_ci		break;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci	release_sock(sk);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	return err;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ciint dccp_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
57662306a36Sopenharmony_ci		    unsigned int optlen)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	if (level != SOL_DCCP)
57962306a36Sopenharmony_ci		return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,
58062306a36Sopenharmony_ci							     optname, optval,
58162306a36Sopenharmony_ci							     optlen);
58262306a36Sopenharmony_ci	return do_dccp_setsockopt(sk, level, optname, optval, optlen);
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_setsockopt);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic int dccp_getsockopt_service(struct sock *sk, int len,
58862306a36Sopenharmony_ci				   __be32 __user *optval,
58962306a36Sopenharmony_ci				   int __user *optlen)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	const struct dccp_sock *dp = dccp_sk(sk);
59262306a36Sopenharmony_ci	const struct dccp_service_list *sl;
59362306a36Sopenharmony_ci	int err = -ENOENT, slen = 0, total_len = sizeof(u32);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	lock_sock(sk);
59662306a36Sopenharmony_ci	if ((sl = dp->dccps_service_list) != NULL) {
59762306a36Sopenharmony_ci		slen = sl->dccpsl_nr * sizeof(u32);
59862306a36Sopenharmony_ci		total_len += slen;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	err = -EINVAL;
60262306a36Sopenharmony_ci	if (total_len > len)
60362306a36Sopenharmony_ci		goto out;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	err = 0;
60662306a36Sopenharmony_ci	if (put_user(total_len, optlen) ||
60762306a36Sopenharmony_ci	    put_user(dp->dccps_service, optval) ||
60862306a36Sopenharmony_ci	    (sl != NULL && copy_to_user(optval + 1, sl->dccpsl_list, slen)))
60962306a36Sopenharmony_ci		err = -EFAULT;
61062306a36Sopenharmony_ciout:
61162306a36Sopenharmony_ci	release_sock(sk);
61262306a36Sopenharmony_ci	return err;
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic int do_dccp_getsockopt(struct sock *sk, int level, int optname,
61662306a36Sopenharmony_ci		    char __user *optval, int __user *optlen)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	struct dccp_sock *dp;
61962306a36Sopenharmony_ci	int val, len;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	if (get_user(len, optlen))
62262306a36Sopenharmony_ci		return -EFAULT;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (len < (int)sizeof(int))
62562306a36Sopenharmony_ci		return -EINVAL;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	dp = dccp_sk(sk);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	switch (optname) {
63062306a36Sopenharmony_ci	case DCCP_SOCKOPT_PACKET_SIZE:
63162306a36Sopenharmony_ci		DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
63262306a36Sopenharmony_ci		return 0;
63362306a36Sopenharmony_ci	case DCCP_SOCKOPT_SERVICE:
63462306a36Sopenharmony_ci		return dccp_getsockopt_service(sk, len,
63562306a36Sopenharmony_ci					       (__be32 __user *)optval, optlen);
63662306a36Sopenharmony_ci	case DCCP_SOCKOPT_GET_CUR_MPS:
63762306a36Sopenharmony_ci		val = READ_ONCE(dp->dccps_mss_cache);
63862306a36Sopenharmony_ci		break;
63962306a36Sopenharmony_ci	case DCCP_SOCKOPT_AVAILABLE_CCIDS:
64062306a36Sopenharmony_ci		return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen);
64162306a36Sopenharmony_ci	case DCCP_SOCKOPT_TX_CCID:
64262306a36Sopenharmony_ci		val = ccid_get_current_tx_ccid(dp);
64362306a36Sopenharmony_ci		if (val < 0)
64462306a36Sopenharmony_ci			return -ENOPROTOOPT;
64562306a36Sopenharmony_ci		break;
64662306a36Sopenharmony_ci	case DCCP_SOCKOPT_RX_CCID:
64762306a36Sopenharmony_ci		val = ccid_get_current_rx_ccid(dp);
64862306a36Sopenharmony_ci		if (val < 0)
64962306a36Sopenharmony_ci			return -ENOPROTOOPT;
65062306a36Sopenharmony_ci		break;
65162306a36Sopenharmony_ci	case DCCP_SOCKOPT_SERVER_TIMEWAIT:
65262306a36Sopenharmony_ci		val = dp->dccps_server_timewait;
65362306a36Sopenharmony_ci		break;
65462306a36Sopenharmony_ci	case DCCP_SOCKOPT_SEND_CSCOV:
65562306a36Sopenharmony_ci		val = dp->dccps_pcslen;
65662306a36Sopenharmony_ci		break;
65762306a36Sopenharmony_ci	case DCCP_SOCKOPT_RECV_CSCOV:
65862306a36Sopenharmony_ci		val = dp->dccps_pcrlen;
65962306a36Sopenharmony_ci		break;
66062306a36Sopenharmony_ci	case DCCP_SOCKOPT_QPOLICY_ID:
66162306a36Sopenharmony_ci		val = dp->dccps_qpolicy;
66262306a36Sopenharmony_ci		break;
66362306a36Sopenharmony_ci	case DCCP_SOCKOPT_QPOLICY_TXQLEN:
66462306a36Sopenharmony_ci		val = dp->dccps_tx_qlen;
66562306a36Sopenharmony_ci		break;
66662306a36Sopenharmony_ci	case 128 ... 191:
66762306a36Sopenharmony_ci		return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname,
66862306a36Sopenharmony_ci					     len, (u32 __user *)optval, optlen);
66962306a36Sopenharmony_ci	case 192 ... 255:
67062306a36Sopenharmony_ci		return ccid_hc_tx_getsockopt(dp->dccps_hc_tx_ccid, sk, optname,
67162306a36Sopenharmony_ci					     len, (u32 __user *)optval, optlen);
67262306a36Sopenharmony_ci	default:
67362306a36Sopenharmony_ci		return -ENOPROTOOPT;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	len = sizeof(val);
67762306a36Sopenharmony_ci	if (put_user(len, optlen) || copy_to_user(optval, &val, len))
67862306a36Sopenharmony_ci		return -EFAULT;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	return 0;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ciint dccp_getsockopt(struct sock *sk, int level, int optname,
68462306a36Sopenharmony_ci		    char __user *optval, int __user *optlen)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	if (level != SOL_DCCP)
68762306a36Sopenharmony_ci		return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level,
68862306a36Sopenharmony_ci							     optname, optval,
68962306a36Sopenharmony_ci							     optlen);
69062306a36Sopenharmony_ci	return do_dccp_getsockopt(sk, level, optname, optval, optlen);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_getsockopt);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	struct cmsghdr *cmsg;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/*
70062306a36Sopenharmony_ci	 * Assign an (opaque) qpolicy priority value to skb->priority.
70162306a36Sopenharmony_ci	 *
70262306a36Sopenharmony_ci	 * We are overloading this skb field for use with the qpolicy subystem.
70362306a36Sopenharmony_ci	 * The skb->priority is normally used for the SO_PRIORITY option, which
70462306a36Sopenharmony_ci	 * is initialised from sk_priority. Since the assignment of sk_priority
70562306a36Sopenharmony_ci	 * to skb->priority happens later (on layer 3), we overload this field
70662306a36Sopenharmony_ci	 * for use with queueing priorities as long as the skb is on layer 4.
70762306a36Sopenharmony_ci	 * The default priority value (if nothing is set) is 0.
70862306a36Sopenharmony_ci	 */
70962306a36Sopenharmony_ci	skb->priority = 0;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	for_each_cmsghdr(cmsg, msg) {
71262306a36Sopenharmony_ci		if (!CMSG_OK(msg, cmsg))
71362306a36Sopenharmony_ci			return -EINVAL;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci		if (cmsg->cmsg_level != SOL_DCCP)
71662306a36Sopenharmony_ci			continue;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		if (cmsg->cmsg_type <= DCCP_SCM_QPOLICY_MAX &&
71962306a36Sopenharmony_ci		    !dccp_qpolicy_param_ok(skb->sk, cmsg->cmsg_type))
72062306a36Sopenharmony_ci			return -EINVAL;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci		switch (cmsg->cmsg_type) {
72362306a36Sopenharmony_ci		case DCCP_SCM_PRIORITY:
72462306a36Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(sizeof(__u32)))
72562306a36Sopenharmony_ci				return -EINVAL;
72662306a36Sopenharmony_ci			skb->priority = *(__u32 *)CMSG_DATA(cmsg);
72762306a36Sopenharmony_ci			break;
72862306a36Sopenharmony_ci		default:
72962306a36Sopenharmony_ci			return -EINVAL;
73062306a36Sopenharmony_ci		}
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci	return 0;
73362306a36Sopenharmony_ci}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ciint dccp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	const struct dccp_sock *dp = dccp_sk(sk);
73862306a36Sopenharmony_ci	const int flags = msg->msg_flags;
73962306a36Sopenharmony_ci	const int noblock = flags & MSG_DONTWAIT;
74062306a36Sopenharmony_ci	struct sk_buff *skb;
74162306a36Sopenharmony_ci	int rc, size;
74262306a36Sopenharmony_ci	long timeo;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	trace_dccp_probe(sk, len);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	if (len > READ_ONCE(dp->dccps_mss_cache))
74762306a36Sopenharmony_ci		return -EMSGSIZE;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	lock_sock(sk);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	timeo = sock_sndtimeo(sk, noblock);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	/*
75462306a36Sopenharmony_ci	 * We have to use sk_stream_wait_connect here to set sk_write_pending,
75562306a36Sopenharmony_ci	 * so that the trick in dccp_rcv_request_sent_state_process.
75662306a36Sopenharmony_ci	 */
75762306a36Sopenharmony_ci	/* Wait for a connection to finish. */
75862306a36Sopenharmony_ci	if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN))
75962306a36Sopenharmony_ci		if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0)
76062306a36Sopenharmony_ci			goto out_release;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	size = sk->sk_prot->max_header + len;
76362306a36Sopenharmony_ci	release_sock(sk);
76462306a36Sopenharmony_ci	skb = sock_alloc_send_skb(sk, size, noblock, &rc);
76562306a36Sopenharmony_ci	lock_sock(sk);
76662306a36Sopenharmony_ci	if (skb == NULL)
76762306a36Sopenharmony_ci		goto out_release;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	if (dccp_qpolicy_full(sk)) {
77062306a36Sopenharmony_ci		rc = -EAGAIN;
77162306a36Sopenharmony_ci		goto out_discard;
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (sk->sk_state == DCCP_CLOSED) {
77562306a36Sopenharmony_ci		rc = -ENOTCONN;
77662306a36Sopenharmony_ci		goto out_discard;
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	/* We need to check dccps_mss_cache after socket is locked. */
78062306a36Sopenharmony_ci	if (len > dp->dccps_mss_cache) {
78162306a36Sopenharmony_ci		rc = -EMSGSIZE;
78262306a36Sopenharmony_ci		goto out_discard;
78362306a36Sopenharmony_ci	}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	skb_reserve(skb, sk->sk_prot->max_header);
78662306a36Sopenharmony_ci	rc = memcpy_from_msg(skb_put(skb, len), msg, len);
78762306a36Sopenharmony_ci	if (rc != 0)
78862306a36Sopenharmony_ci		goto out_discard;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	rc = dccp_msghdr_parse(msg, skb);
79162306a36Sopenharmony_ci	if (rc != 0)
79262306a36Sopenharmony_ci		goto out_discard;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	dccp_qpolicy_push(sk, skb);
79562306a36Sopenharmony_ci	/*
79662306a36Sopenharmony_ci	 * The xmit_timer is set if the TX CCID is rate-based and will expire
79762306a36Sopenharmony_ci	 * when congestion control permits to release further packets into the
79862306a36Sopenharmony_ci	 * network. Window-based CCIDs do not use this timer.
79962306a36Sopenharmony_ci	 */
80062306a36Sopenharmony_ci	if (!timer_pending(&dp->dccps_xmit_timer))
80162306a36Sopenharmony_ci		dccp_write_xmit(sk);
80262306a36Sopenharmony_ciout_release:
80362306a36Sopenharmony_ci	release_sock(sk);
80462306a36Sopenharmony_ci	return rc ? : len;
80562306a36Sopenharmony_ciout_discard:
80662306a36Sopenharmony_ci	kfree_skb(skb);
80762306a36Sopenharmony_ci	goto out_release;
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_sendmsg);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ciint dccp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags,
81362306a36Sopenharmony_ci		 int *addr_len)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	const struct dccp_hdr *dh;
81662306a36Sopenharmony_ci	long timeo;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	lock_sock(sk);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	if (sk->sk_state == DCCP_LISTEN) {
82162306a36Sopenharmony_ci		len = -ENOTCONN;
82262306a36Sopenharmony_ci		goto out;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	do {
82862306a36Sopenharmony_ci		struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		if (skb == NULL)
83162306a36Sopenharmony_ci			goto verify_sock_status;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		dh = dccp_hdr(skb);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci		switch (dh->dccph_type) {
83662306a36Sopenharmony_ci		case DCCP_PKT_DATA:
83762306a36Sopenharmony_ci		case DCCP_PKT_DATAACK:
83862306a36Sopenharmony_ci			goto found_ok_skb;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci		case DCCP_PKT_CLOSE:
84162306a36Sopenharmony_ci		case DCCP_PKT_CLOSEREQ:
84262306a36Sopenharmony_ci			if (!(flags & MSG_PEEK))
84362306a36Sopenharmony_ci				dccp_finish_passive_close(sk);
84462306a36Sopenharmony_ci			fallthrough;
84562306a36Sopenharmony_ci		case DCCP_PKT_RESET:
84662306a36Sopenharmony_ci			dccp_pr_debug("found fin (%s) ok!\n",
84762306a36Sopenharmony_ci				      dccp_packet_name(dh->dccph_type));
84862306a36Sopenharmony_ci			len = 0;
84962306a36Sopenharmony_ci			goto found_fin_ok;
85062306a36Sopenharmony_ci		default:
85162306a36Sopenharmony_ci			dccp_pr_debug("packet_type=%s\n",
85262306a36Sopenharmony_ci				      dccp_packet_name(dh->dccph_type));
85362306a36Sopenharmony_ci			sk_eat_skb(sk, skb);
85462306a36Sopenharmony_ci		}
85562306a36Sopenharmony_civerify_sock_status:
85662306a36Sopenharmony_ci		if (sock_flag(sk, SOCK_DONE)) {
85762306a36Sopenharmony_ci			len = 0;
85862306a36Sopenharmony_ci			break;
85962306a36Sopenharmony_ci		}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci		if (sk->sk_err) {
86262306a36Sopenharmony_ci			len = sock_error(sk);
86362306a36Sopenharmony_ci			break;
86462306a36Sopenharmony_ci		}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci		if (sk->sk_shutdown & RCV_SHUTDOWN) {
86762306a36Sopenharmony_ci			len = 0;
86862306a36Sopenharmony_ci			break;
86962306a36Sopenharmony_ci		}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		if (sk->sk_state == DCCP_CLOSED) {
87262306a36Sopenharmony_ci			if (!sock_flag(sk, SOCK_DONE)) {
87362306a36Sopenharmony_ci				/* This occurs when user tries to read
87462306a36Sopenharmony_ci				 * from never connected socket.
87562306a36Sopenharmony_ci				 */
87662306a36Sopenharmony_ci				len = -ENOTCONN;
87762306a36Sopenharmony_ci				break;
87862306a36Sopenharmony_ci			}
87962306a36Sopenharmony_ci			len = 0;
88062306a36Sopenharmony_ci			break;
88162306a36Sopenharmony_ci		}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci		if (!timeo) {
88462306a36Sopenharmony_ci			len = -EAGAIN;
88562306a36Sopenharmony_ci			break;
88662306a36Sopenharmony_ci		}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci		if (signal_pending(current)) {
88962306a36Sopenharmony_ci			len = sock_intr_errno(timeo);
89062306a36Sopenharmony_ci			break;
89162306a36Sopenharmony_ci		}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci		sk_wait_data(sk, &timeo, NULL);
89462306a36Sopenharmony_ci		continue;
89562306a36Sopenharmony_ci	found_ok_skb:
89662306a36Sopenharmony_ci		if (len > skb->len)
89762306a36Sopenharmony_ci			len = skb->len;
89862306a36Sopenharmony_ci		else if (len < skb->len)
89962306a36Sopenharmony_ci			msg->msg_flags |= MSG_TRUNC;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci		if (skb_copy_datagram_msg(skb, 0, msg, len)) {
90262306a36Sopenharmony_ci			/* Exception. Bailout! */
90362306a36Sopenharmony_ci			len = -EFAULT;
90462306a36Sopenharmony_ci			break;
90562306a36Sopenharmony_ci		}
90662306a36Sopenharmony_ci		if (flags & MSG_TRUNC)
90762306a36Sopenharmony_ci			len = skb->len;
90862306a36Sopenharmony_ci	found_fin_ok:
90962306a36Sopenharmony_ci		if (!(flags & MSG_PEEK))
91062306a36Sopenharmony_ci			sk_eat_skb(sk, skb);
91162306a36Sopenharmony_ci		break;
91262306a36Sopenharmony_ci	} while (1);
91362306a36Sopenharmony_ciout:
91462306a36Sopenharmony_ci	release_sock(sk);
91562306a36Sopenharmony_ci	return len;
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_recvmsg);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ciint inet_dccp_listen(struct socket *sock, int backlog)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct sock *sk = sock->sk;
92362306a36Sopenharmony_ci	unsigned char old_state;
92462306a36Sopenharmony_ci	int err;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	lock_sock(sk);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	err = -EINVAL;
92962306a36Sopenharmony_ci	if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP)
93062306a36Sopenharmony_ci		goto out;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	old_state = sk->sk_state;
93362306a36Sopenharmony_ci	if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN)))
93462306a36Sopenharmony_ci		goto out;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	WRITE_ONCE(sk->sk_max_ack_backlog, backlog);
93762306a36Sopenharmony_ci	/* Really, if the socket is already in listen state
93862306a36Sopenharmony_ci	 * we can only allow the backlog to be adjusted.
93962306a36Sopenharmony_ci	 */
94062306a36Sopenharmony_ci	if (old_state != DCCP_LISTEN) {
94162306a36Sopenharmony_ci		struct dccp_sock *dp = dccp_sk(sk);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci		dp->dccps_role = DCCP_ROLE_LISTEN;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci		/* do not start to listen if feature negotiation setup fails */
94662306a36Sopenharmony_ci		if (dccp_feat_finalise_settings(dp)) {
94762306a36Sopenharmony_ci			err = -EPROTO;
94862306a36Sopenharmony_ci			goto out;
94962306a36Sopenharmony_ci		}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci		err = inet_csk_listen_start(sk);
95262306a36Sopenharmony_ci		if (err)
95362306a36Sopenharmony_ci			goto out;
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci	err = 0;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ciout:
95862306a36Sopenharmony_ci	release_sock(sk);
95962306a36Sopenharmony_ci	return err;
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_dccp_listen);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_cistatic void dccp_terminate_connection(struct sock *sk)
96562306a36Sopenharmony_ci{
96662306a36Sopenharmony_ci	u8 next_state = DCCP_CLOSED;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	switch (sk->sk_state) {
96962306a36Sopenharmony_ci	case DCCP_PASSIVE_CLOSE:
97062306a36Sopenharmony_ci	case DCCP_PASSIVE_CLOSEREQ:
97162306a36Sopenharmony_ci		dccp_finish_passive_close(sk);
97262306a36Sopenharmony_ci		break;
97362306a36Sopenharmony_ci	case DCCP_PARTOPEN:
97462306a36Sopenharmony_ci		dccp_pr_debug("Stop PARTOPEN timer (%p)\n", sk);
97562306a36Sopenharmony_ci		inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);
97662306a36Sopenharmony_ci		fallthrough;
97762306a36Sopenharmony_ci	case DCCP_OPEN:
97862306a36Sopenharmony_ci		dccp_send_close(sk, 1);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci		if (dccp_sk(sk)->dccps_role == DCCP_ROLE_SERVER &&
98162306a36Sopenharmony_ci		    !dccp_sk(sk)->dccps_server_timewait)
98262306a36Sopenharmony_ci			next_state = DCCP_ACTIVE_CLOSEREQ;
98362306a36Sopenharmony_ci		else
98462306a36Sopenharmony_ci			next_state = DCCP_CLOSING;
98562306a36Sopenharmony_ci		fallthrough;
98662306a36Sopenharmony_ci	default:
98762306a36Sopenharmony_ci		dccp_set_state(sk, next_state);
98862306a36Sopenharmony_ci	}
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_civoid dccp_close(struct sock *sk, long timeout)
99262306a36Sopenharmony_ci{
99362306a36Sopenharmony_ci	struct dccp_sock *dp = dccp_sk(sk);
99462306a36Sopenharmony_ci	struct sk_buff *skb;
99562306a36Sopenharmony_ci	u32 data_was_unread = 0;
99662306a36Sopenharmony_ci	int state;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	lock_sock(sk);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	sk->sk_shutdown = SHUTDOWN_MASK;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	if (sk->sk_state == DCCP_LISTEN) {
100362306a36Sopenharmony_ci		dccp_set_state(sk, DCCP_CLOSED);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci		/* Special case. */
100662306a36Sopenharmony_ci		inet_csk_listen_stop(sk);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci		goto adjudge_to_death;
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	sk_stop_timer(sk, &dp->dccps_xmit_timer);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	/*
101462306a36Sopenharmony_ci	 * We need to flush the recv. buffs.  We do this only on the
101562306a36Sopenharmony_ci	 * descriptor close, not protocol-sourced closes, because the
101662306a36Sopenharmony_ci	  *reader process may not have drained the data yet!
101762306a36Sopenharmony_ci	 */
101862306a36Sopenharmony_ci	while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
101962306a36Sopenharmony_ci		data_was_unread += skb->len;
102062306a36Sopenharmony_ci		__kfree_skb(skb);
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* If socket has been already reset kill it. */
102462306a36Sopenharmony_ci	if (sk->sk_state == DCCP_CLOSED)
102562306a36Sopenharmony_ci		goto adjudge_to_death;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	if (data_was_unread) {
102862306a36Sopenharmony_ci		/* Unread data was tossed, send an appropriate Reset Code */
102962306a36Sopenharmony_ci		DCCP_WARN("ABORT with %u bytes unread\n", data_was_unread);
103062306a36Sopenharmony_ci		dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
103162306a36Sopenharmony_ci		dccp_set_state(sk, DCCP_CLOSED);
103262306a36Sopenharmony_ci	} else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
103362306a36Sopenharmony_ci		/* Check zero linger _after_ checking for unread data. */
103462306a36Sopenharmony_ci		sk->sk_prot->disconnect(sk, 0);
103562306a36Sopenharmony_ci	} else if (sk->sk_state != DCCP_CLOSED) {
103662306a36Sopenharmony_ci		/*
103762306a36Sopenharmony_ci		 * Normal connection termination. May need to wait if there are
103862306a36Sopenharmony_ci		 * still packets in the TX queue that are delayed by the CCID.
103962306a36Sopenharmony_ci		 */
104062306a36Sopenharmony_ci		dccp_flush_write_queue(sk, &timeout);
104162306a36Sopenharmony_ci		dccp_terminate_connection(sk);
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	/*
104562306a36Sopenharmony_ci	 * Flush write queue. This may be necessary in several cases:
104662306a36Sopenharmony_ci	 * - we have been closed by the peer but still have application data;
104762306a36Sopenharmony_ci	 * - abortive termination (unread data or zero linger time),
104862306a36Sopenharmony_ci	 * - normal termination but queue could not be flushed within time limit
104962306a36Sopenharmony_ci	 */
105062306a36Sopenharmony_ci	__skb_queue_purge(&sk->sk_write_queue);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	sk_stream_wait_close(sk, timeout);
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ciadjudge_to_death:
105562306a36Sopenharmony_ci	state = sk->sk_state;
105662306a36Sopenharmony_ci	sock_hold(sk);
105762306a36Sopenharmony_ci	sock_orphan(sk);
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	/*
106062306a36Sopenharmony_ci	 * It is the last release_sock in its life. It will remove backlog.
106162306a36Sopenharmony_ci	 */
106262306a36Sopenharmony_ci	release_sock(sk);
106362306a36Sopenharmony_ci	/*
106462306a36Sopenharmony_ci	 * Now socket is owned by kernel and we acquire BH lock
106562306a36Sopenharmony_ci	 * to finish close. No need to check for user refs.
106662306a36Sopenharmony_ci	 */
106762306a36Sopenharmony_ci	local_bh_disable();
106862306a36Sopenharmony_ci	bh_lock_sock(sk);
106962306a36Sopenharmony_ci	WARN_ON(sock_owned_by_user(sk));
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	this_cpu_inc(dccp_orphan_count);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	/* Have we already been destroyed by a softirq or backlog? */
107462306a36Sopenharmony_ci	if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
107562306a36Sopenharmony_ci		goto out;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	if (sk->sk_state == DCCP_CLOSED)
107862306a36Sopenharmony_ci		inet_csk_destroy_sock(sk);
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	/* Otherwise, socket is reprieved until protocol close. */
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ciout:
108362306a36Sopenharmony_ci	bh_unlock_sock(sk);
108462306a36Sopenharmony_ci	local_bh_enable();
108562306a36Sopenharmony_ci	sock_put(sk);
108662306a36Sopenharmony_ci}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_close);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_civoid dccp_shutdown(struct sock *sk, int how)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	dccp_pr_debug("called shutdown(%x)\n", how);
109362306a36Sopenharmony_ci}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_shutdown);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cistatic inline int __init dccp_mib_init(void)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	dccp_statistics = alloc_percpu(struct dccp_mib);
110062306a36Sopenharmony_ci	if (!dccp_statistics)
110162306a36Sopenharmony_ci		return -ENOMEM;
110262306a36Sopenharmony_ci	return 0;
110362306a36Sopenharmony_ci}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_cistatic inline void dccp_mib_exit(void)
110662306a36Sopenharmony_ci{
110762306a36Sopenharmony_ci	free_percpu(dccp_statistics);
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_cistatic int thash_entries;
111162306a36Sopenharmony_cimodule_param(thash_entries, int, 0444);
111262306a36Sopenharmony_ciMODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci#ifdef CONFIG_IP_DCCP_DEBUG
111562306a36Sopenharmony_cibool dccp_debug;
111662306a36Sopenharmony_cimodule_param(dccp_debug, bool, 0644);
111762306a36Sopenharmony_ciMODULE_PARM_DESC(dccp_debug, "Enable debug messages");
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_debug);
112062306a36Sopenharmony_ci#endif
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_cistatic int __init dccp_init(void)
112362306a36Sopenharmony_ci{
112462306a36Sopenharmony_ci	unsigned long goal;
112562306a36Sopenharmony_ci	unsigned long nr_pages = totalram_pages();
112662306a36Sopenharmony_ci	int ehash_order, bhash_order, i;
112762306a36Sopenharmony_ci	int rc;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct dccp_skb_cb) >
113062306a36Sopenharmony_ci		     sizeof_field(struct sk_buff, cb));
113162306a36Sopenharmony_ci	rc = inet_hashinfo2_init_mod(&dccp_hashinfo);
113262306a36Sopenharmony_ci	if (rc)
113362306a36Sopenharmony_ci		goto out_fail;
113462306a36Sopenharmony_ci	rc = -ENOBUFS;
113562306a36Sopenharmony_ci	dccp_hashinfo.bind_bucket_cachep =
113662306a36Sopenharmony_ci		kmem_cache_create("dccp_bind_bucket",
113762306a36Sopenharmony_ci				  sizeof(struct inet_bind_bucket), 0,
113862306a36Sopenharmony_ci				  SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, NULL);
113962306a36Sopenharmony_ci	if (!dccp_hashinfo.bind_bucket_cachep)
114062306a36Sopenharmony_ci		goto out_free_hashinfo2;
114162306a36Sopenharmony_ci	dccp_hashinfo.bind2_bucket_cachep =
114262306a36Sopenharmony_ci		kmem_cache_create("dccp_bind2_bucket",
114362306a36Sopenharmony_ci				  sizeof(struct inet_bind2_bucket), 0,
114462306a36Sopenharmony_ci				  SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, NULL);
114562306a36Sopenharmony_ci	if (!dccp_hashinfo.bind2_bucket_cachep)
114662306a36Sopenharmony_ci		goto out_free_bind_bucket_cachep;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	/*
114962306a36Sopenharmony_ci	 * Size and allocate the main established and bind bucket
115062306a36Sopenharmony_ci	 * hash tables.
115162306a36Sopenharmony_ci	 *
115262306a36Sopenharmony_ci	 * The methodology is similar to that of the buffer cache.
115362306a36Sopenharmony_ci	 */
115462306a36Sopenharmony_ci	if (nr_pages >= (128 * 1024))
115562306a36Sopenharmony_ci		goal = nr_pages >> (21 - PAGE_SHIFT);
115662306a36Sopenharmony_ci	else
115762306a36Sopenharmony_ci		goal = nr_pages >> (23 - PAGE_SHIFT);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	if (thash_entries)
116062306a36Sopenharmony_ci		goal = (thash_entries *
116162306a36Sopenharmony_ci			sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT;
116262306a36Sopenharmony_ci	for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++)
116362306a36Sopenharmony_ci		;
116462306a36Sopenharmony_ci	do {
116562306a36Sopenharmony_ci		unsigned long hash_size = (1UL << ehash_order) * PAGE_SIZE /
116662306a36Sopenharmony_ci					sizeof(struct inet_ehash_bucket);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci		while (hash_size & (hash_size - 1))
116962306a36Sopenharmony_ci			hash_size--;
117062306a36Sopenharmony_ci		dccp_hashinfo.ehash_mask = hash_size - 1;
117162306a36Sopenharmony_ci		dccp_hashinfo.ehash = (struct inet_ehash_bucket *)
117262306a36Sopenharmony_ci			__get_free_pages(GFP_ATOMIC|__GFP_NOWARN, ehash_order);
117362306a36Sopenharmony_ci	} while (!dccp_hashinfo.ehash && --ehash_order > 0);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	if (!dccp_hashinfo.ehash) {
117662306a36Sopenharmony_ci		DCCP_CRIT("Failed to allocate DCCP established hash table");
117762306a36Sopenharmony_ci		goto out_free_bind2_bucket_cachep;
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	for (i = 0; i <= dccp_hashinfo.ehash_mask; i++)
118162306a36Sopenharmony_ci		INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].chain, i);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	if (inet_ehash_locks_alloc(&dccp_hashinfo))
118462306a36Sopenharmony_ci			goto out_free_dccp_ehash;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	bhash_order = ehash_order;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	do {
118962306a36Sopenharmony_ci		dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE /
119062306a36Sopenharmony_ci					sizeof(struct inet_bind_hashbucket);
119162306a36Sopenharmony_ci		if ((dccp_hashinfo.bhash_size > (64 * 1024)) &&
119262306a36Sopenharmony_ci		    bhash_order > 0)
119362306a36Sopenharmony_ci			continue;
119462306a36Sopenharmony_ci		dccp_hashinfo.bhash = (struct inet_bind_hashbucket *)
119562306a36Sopenharmony_ci			__get_free_pages(GFP_ATOMIC|__GFP_NOWARN, bhash_order);
119662306a36Sopenharmony_ci	} while (!dccp_hashinfo.bhash && --bhash_order >= 0);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	if (!dccp_hashinfo.bhash) {
119962306a36Sopenharmony_ci		DCCP_CRIT("Failed to allocate DCCP bind hash table");
120062306a36Sopenharmony_ci		goto out_free_dccp_locks;
120162306a36Sopenharmony_ci	}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	dccp_hashinfo.bhash2 = (struct inet_bind_hashbucket *)
120462306a36Sopenharmony_ci		__get_free_pages(GFP_ATOMIC | __GFP_NOWARN, bhash_order);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	if (!dccp_hashinfo.bhash2) {
120762306a36Sopenharmony_ci		DCCP_CRIT("Failed to allocate DCCP bind2 hash table");
120862306a36Sopenharmony_ci		goto out_free_dccp_bhash;
120962306a36Sopenharmony_ci	}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	for (i = 0; i < dccp_hashinfo.bhash_size; i++) {
121262306a36Sopenharmony_ci		spin_lock_init(&dccp_hashinfo.bhash[i].lock);
121362306a36Sopenharmony_ci		INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain);
121462306a36Sopenharmony_ci		spin_lock_init(&dccp_hashinfo.bhash2[i].lock);
121562306a36Sopenharmony_ci		INIT_HLIST_HEAD(&dccp_hashinfo.bhash2[i].chain);
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	dccp_hashinfo.pernet = false;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	rc = dccp_mib_init();
122162306a36Sopenharmony_ci	if (rc)
122262306a36Sopenharmony_ci		goto out_free_dccp_bhash2;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	rc = dccp_ackvec_init();
122562306a36Sopenharmony_ci	if (rc)
122662306a36Sopenharmony_ci		goto out_free_dccp_mib;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	rc = dccp_sysctl_init();
122962306a36Sopenharmony_ci	if (rc)
123062306a36Sopenharmony_ci		goto out_ackvec_exit;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	rc = ccid_initialize_builtins();
123362306a36Sopenharmony_ci	if (rc)
123462306a36Sopenharmony_ci		goto out_sysctl_exit;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	dccp_timestamping_init();
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	return 0;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ciout_sysctl_exit:
124162306a36Sopenharmony_ci	dccp_sysctl_exit();
124262306a36Sopenharmony_ciout_ackvec_exit:
124362306a36Sopenharmony_ci	dccp_ackvec_exit();
124462306a36Sopenharmony_ciout_free_dccp_mib:
124562306a36Sopenharmony_ci	dccp_mib_exit();
124662306a36Sopenharmony_ciout_free_dccp_bhash2:
124762306a36Sopenharmony_ci	free_pages((unsigned long)dccp_hashinfo.bhash2, bhash_order);
124862306a36Sopenharmony_ciout_free_dccp_bhash:
124962306a36Sopenharmony_ci	free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
125062306a36Sopenharmony_ciout_free_dccp_locks:
125162306a36Sopenharmony_ci	inet_ehash_locks_free(&dccp_hashinfo);
125262306a36Sopenharmony_ciout_free_dccp_ehash:
125362306a36Sopenharmony_ci	free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order);
125462306a36Sopenharmony_ciout_free_bind2_bucket_cachep:
125562306a36Sopenharmony_ci	kmem_cache_destroy(dccp_hashinfo.bind2_bucket_cachep);
125662306a36Sopenharmony_ciout_free_bind_bucket_cachep:
125762306a36Sopenharmony_ci	kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
125862306a36Sopenharmony_ciout_free_hashinfo2:
125962306a36Sopenharmony_ci	inet_hashinfo2_free_mod(&dccp_hashinfo);
126062306a36Sopenharmony_ciout_fail:
126162306a36Sopenharmony_ci	dccp_hashinfo.bhash = NULL;
126262306a36Sopenharmony_ci	dccp_hashinfo.bhash2 = NULL;
126362306a36Sopenharmony_ci	dccp_hashinfo.ehash = NULL;
126462306a36Sopenharmony_ci	dccp_hashinfo.bind_bucket_cachep = NULL;
126562306a36Sopenharmony_ci	dccp_hashinfo.bind2_bucket_cachep = NULL;
126662306a36Sopenharmony_ci	return rc;
126762306a36Sopenharmony_ci}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_cistatic void __exit dccp_fini(void)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	int bhash_order = get_order(dccp_hashinfo.bhash_size *
127262306a36Sopenharmony_ci				    sizeof(struct inet_bind_hashbucket));
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	ccid_cleanup_builtins();
127562306a36Sopenharmony_ci	dccp_mib_exit();
127662306a36Sopenharmony_ci	free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
127762306a36Sopenharmony_ci	free_pages((unsigned long)dccp_hashinfo.bhash2, bhash_order);
127862306a36Sopenharmony_ci	free_pages((unsigned long)dccp_hashinfo.ehash,
127962306a36Sopenharmony_ci		   get_order((dccp_hashinfo.ehash_mask + 1) *
128062306a36Sopenharmony_ci			     sizeof(struct inet_ehash_bucket)));
128162306a36Sopenharmony_ci	inet_ehash_locks_free(&dccp_hashinfo);
128262306a36Sopenharmony_ci	kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
128362306a36Sopenharmony_ci	dccp_ackvec_exit();
128462306a36Sopenharmony_ci	dccp_sysctl_exit();
128562306a36Sopenharmony_ci	inet_hashinfo2_free_mod(&dccp_hashinfo);
128662306a36Sopenharmony_ci}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_cimodule_init(dccp_init);
128962306a36Sopenharmony_cimodule_exit(dccp_fini);
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
129262306a36Sopenharmony_ciMODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>");
129362306a36Sopenharmony_ciMODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol");
1294