162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  net/dccp/minisocks.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/gfp.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/skbuff.h>
1362306a36Sopenharmony_ci#include <linux/timer.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <net/sock.h>
1662306a36Sopenharmony_ci#include <net/xfrm.h>
1762306a36Sopenharmony_ci#include <net/inet_timewait_sock.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "ackvec.h"
2062306a36Sopenharmony_ci#include "ccid.h"
2162306a36Sopenharmony_ci#include "dccp.h"
2262306a36Sopenharmony_ci#include "feat.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct inet_timewait_death_row dccp_death_row = {
2562306a36Sopenharmony_ci	.tw_refcount = REFCOUNT_INIT(1),
2662306a36Sopenharmony_ci	.sysctl_max_tw_buckets = NR_FILE * 2,
2762306a36Sopenharmony_ci	.hashinfo	= &dccp_hashinfo,
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_death_row);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_civoid dccp_time_wait(struct sock *sk, int state, int timeo)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct inet_timewait_sock *tw;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	tw = inet_twsk_alloc(sk, &dccp_death_row, state);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (tw != NULL) {
3962306a36Sopenharmony_ci		const struct inet_connection_sock *icsk = inet_csk(sk);
4062306a36Sopenharmony_ci		const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1);
4162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
4262306a36Sopenharmony_ci		if (tw->tw_family == PF_INET6) {
4362306a36Sopenharmony_ci			tw->tw_v6_daddr = sk->sk_v6_daddr;
4462306a36Sopenharmony_ci			tw->tw_v6_rcv_saddr = sk->sk_v6_rcv_saddr;
4562306a36Sopenharmony_ci			tw->tw_ipv6only = sk->sk_ipv6only;
4662306a36Sopenharmony_ci		}
4762306a36Sopenharmony_ci#endif
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci		/* Get the TIME_WAIT timeout firing. */
5062306a36Sopenharmony_ci		if (timeo < rto)
5162306a36Sopenharmony_ci			timeo = rto;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci		if (state == DCCP_TIME_WAIT)
5462306a36Sopenharmony_ci			timeo = DCCP_TIMEWAIT_LEN;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		/* tw_timer is pinned, so we need to make sure BH are disabled
5762306a36Sopenharmony_ci		 * in following section, otherwise timer handler could run before
5862306a36Sopenharmony_ci		 * we complete the initialization.
5962306a36Sopenharmony_ci		 */
6062306a36Sopenharmony_ci		local_bh_disable();
6162306a36Sopenharmony_ci		inet_twsk_schedule(tw, timeo);
6262306a36Sopenharmony_ci		/* Linkage updates.
6362306a36Sopenharmony_ci		 * Note that access to tw after this point is illegal.
6462306a36Sopenharmony_ci		 */
6562306a36Sopenharmony_ci		inet_twsk_hashdance(tw, sk, &dccp_hashinfo);
6662306a36Sopenharmony_ci		local_bh_enable();
6762306a36Sopenharmony_ci	} else {
6862306a36Sopenharmony_ci		/* Sorry, if we're out of memory, just CLOSE this
6962306a36Sopenharmony_ci		 * socket up.  We've got bigger problems than
7062306a36Sopenharmony_ci		 * non-graceful socket closings.
7162306a36Sopenharmony_ci		 */
7262306a36Sopenharmony_ci		DCCP_WARN("time wait bucket table overflow\n");
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	dccp_done(sk);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistruct sock *dccp_create_openreq_child(const struct sock *sk,
7962306a36Sopenharmony_ci				       const struct request_sock *req,
8062306a36Sopenharmony_ci				       const struct sk_buff *skb)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	/*
8362306a36Sopenharmony_ci	 * Step 3: Process LISTEN state
8462306a36Sopenharmony_ci	 *
8562306a36Sopenharmony_ci	 *   (* Generate a new socket and switch to that socket *)
8662306a36Sopenharmony_ci	 *   Set S := new socket for this port pair
8762306a36Sopenharmony_ci	 */
8862306a36Sopenharmony_ci	struct sock *newsk = inet_csk_clone_lock(sk, req, GFP_ATOMIC);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (newsk != NULL) {
9162306a36Sopenharmony_ci		struct dccp_request_sock *dreq = dccp_rsk(req);
9262306a36Sopenharmony_ci		struct inet_connection_sock *newicsk = inet_csk(newsk);
9362306a36Sopenharmony_ci		struct dccp_sock *newdp = dccp_sk(newsk);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		newdp->dccps_role	    = DCCP_ROLE_SERVER;
9662306a36Sopenharmony_ci		newdp->dccps_hc_rx_ackvec   = NULL;
9762306a36Sopenharmony_ci		newdp->dccps_service_list   = NULL;
9862306a36Sopenharmony_ci		newdp->dccps_hc_rx_ccid     = NULL;
9962306a36Sopenharmony_ci		newdp->dccps_hc_tx_ccid     = NULL;
10062306a36Sopenharmony_ci		newdp->dccps_service	    = dreq->dreq_service;
10162306a36Sopenharmony_ci		newdp->dccps_timestamp_echo = dreq->dreq_timestamp_echo;
10262306a36Sopenharmony_ci		newdp->dccps_timestamp_time = dreq->dreq_timestamp_time;
10362306a36Sopenharmony_ci		newicsk->icsk_rto	    = DCCP_TIMEOUT_INIT;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		INIT_LIST_HEAD(&newdp->dccps_featneg);
10662306a36Sopenharmony_ci		/*
10762306a36Sopenharmony_ci		 * Step 3: Process LISTEN state
10862306a36Sopenharmony_ci		 *
10962306a36Sopenharmony_ci		 *    Choose S.ISS (initial seqno) or set from Init Cookies
11062306a36Sopenharmony_ci		 *    Initialize S.GAR := S.ISS
11162306a36Sopenharmony_ci		 *    Set S.ISR, S.GSR from packet (or Init Cookies)
11262306a36Sopenharmony_ci		 *
11362306a36Sopenharmony_ci		 *    Setting AWL/AWH and SWL/SWH happens as part of the feature
11462306a36Sopenharmony_ci		 *    activation below, as these windows all depend on the local
11562306a36Sopenharmony_ci		 *    and remote Sequence Window feature values (7.5.2).
11662306a36Sopenharmony_ci		 */
11762306a36Sopenharmony_ci		newdp->dccps_iss = dreq->dreq_iss;
11862306a36Sopenharmony_ci		newdp->dccps_gss = dreq->dreq_gss;
11962306a36Sopenharmony_ci		newdp->dccps_gar = newdp->dccps_iss;
12062306a36Sopenharmony_ci		newdp->dccps_isr = dreq->dreq_isr;
12162306a36Sopenharmony_ci		newdp->dccps_gsr = dreq->dreq_gsr;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		/*
12462306a36Sopenharmony_ci		 * Activate features: initialise CCIDs, sequence windows etc.
12562306a36Sopenharmony_ci		 */
12662306a36Sopenharmony_ci		if (dccp_feat_activate_values(newsk, &dreq->dreq_featneg)) {
12762306a36Sopenharmony_ci			sk_free_unlock_clone(newsk);
12862306a36Sopenharmony_ci			return NULL;
12962306a36Sopenharmony_ci		}
13062306a36Sopenharmony_ci		dccp_init_xmit_timers(newsk);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci		__DCCP_INC_STATS(DCCP_MIB_PASSIVEOPENS);
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	return newsk;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_create_openreq_child);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/*
14062306a36Sopenharmony_ci * Process an incoming packet for RESPOND sockets represented
14162306a36Sopenharmony_ci * as an request_sock.
14262306a36Sopenharmony_ci */
14362306a36Sopenharmony_cistruct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
14462306a36Sopenharmony_ci			    struct request_sock *req)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct sock *child = NULL;
14762306a36Sopenharmony_ci	struct dccp_request_sock *dreq = dccp_rsk(req);
14862306a36Sopenharmony_ci	bool own_req;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* TCP/DCCP listeners became lockless.
15162306a36Sopenharmony_ci	 * DCCP stores complex state in its request_sock, so we need
15262306a36Sopenharmony_ci	 * a protection for them, now this code runs without being protected
15362306a36Sopenharmony_ci	 * by the parent (listener) lock.
15462306a36Sopenharmony_ci	 */
15562306a36Sopenharmony_ci	spin_lock_bh(&dreq->dreq_lock);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* Check for retransmitted REQUEST */
15862306a36Sopenharmony_ci	if (dccp_hdr(skb)->dccph_type == DCCP_PKT_REQUEST) {
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		if (after48(DCCP_SKB_CB(skb)->dccpd_seq, dreq->dreq_gsr)) {
16162306a36Sopenharmony_ci			dccp_pr_debug("Retransmitted REQUEST\n");
16262306a36Sopenharmony_ci			dreq->dreq_gsr = DCCP_SKB_CB(skb)->dccpd_seq;
16362306a36Sopenharmony_ci			/*
16462306a36Sopenharmony_ci			 * Send another RESPONSE packet
16562306a36Sopenharmony_ci			 * To protect against Request floods, increment retrans
16662306a36Sopenharmony_ci			 * counter (backoff, monitored by dccp_response_timer).
16762306a36Sopenharmony_ci			 */
16862306a36Sopenharmony_ci			inet_rtx_syn_ack(sk, req);
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci		/* Network Duplicate, discard packet */
17162306a36Sopenharmony_ci		goto out;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_PACKET_ERROR;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (dccp_hdr(skb)->dccph_type != DCCP_PKT_ACK &&
17762306a36Sopenharmony_ci	    dccp_hdr(skb)->dccph_type != DCCP_PKT_DATAACK)
17862306a36Sopenharmony_ci		goto drop;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* Invalid ACK */
18162306a36Sopenharmony_ci	if (!between48(DCCP_SKB_CB(skb)->dccpd_ack_seq,
18262306a36Sopenharmony_ci				dreq->dreq_iss, dreq->dreq_gss)) {
18362306a36Sopenharmony_ci		dccp_pr_debug("Invalid ACK number: ack_seq=%llu, "
18462306a36Sopenharmony_ci			      "dreq_iss=%llu, dreq_gss=%llu\n",
18562306a36Sopenharmony_ci			      (unsigned long long)
18662306a36Sopenharmony_ci			      DCCP_SKB_CB(skb)->dccpd_ack_seq,
18762306a36Sopenharmony_ci			      (unsigned long long) dreq->dreq_iss,
18862306a36Sopenharmony_ci			      (unsigned long long) dreq->dreq_gss);
18962306a36Sopenharmony_ci		goto drop;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (dccp_parse_options(sk, dreq, skb))
19362306a36Sopenharmony_ci		 goto drop;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
19662306a36Sopenharmony_ci							 req, &own_req);
19762306a36Sopenharmony_ci	if (child) {
19862306a36Sopenharmony_ci		child = inet_csk_complete_hashdance(sk, child, req, own_req);
19962306a36Sopenharmony_ci		goto out;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY;
20362306a36Sopenharmony_cidrop:
20462306a36Sopenharmony_ci	if (dccp_hdr(skb)->dccph_type != DCCP_PKT_RESET)
20562306a36Sopenharmony_ci		req->rsk_ops->send_reset(sk, skb);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	inet_csk_reqsk_queue_drop(sk, req);
20862306a36Sopenharmony_ciout:
20962306a36Sopenharmony_ci	spin_unlock_bh(&dreq->dreq_lock);
21062306a36Sopenharmony_ci	return child;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_check_req);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/*
21662306a36Sopenharmony_ci *  Queue segment on the new socket if the new socket is active,
21762306a36Sopenharmony_ci *  otherwise we just shortcircuit this and continue with
21862306a36Sopenharmony_ci *  the new socket.
21962306a36Sopenharmony_ci */
22062306a36Sopenharmony_ciint dccp_child_process(struct sock *parent, struct sock *child,
22162306a36Sopenharmony_ci		       struct sk_buff *skb)
22262306a36Sopenharmony_ci	__releases(child)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	int ret = 0;
22562306a36Sopenharmony_ci	const int state = child->sk_state;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (!sock_owned_by_user(child)) {
22862306a36Sopenharmony_ci		ret = dccp_rcv_state_process(child, skb, dccp_hdr(skb),
22962306a36Sopenharmony_ci					     skb->len);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		/* Wakeup parent, send SIGIO */
23262306a36Sopenharmony_ci		if (state == DCCP_RESPOND && child->sk_state != state)
23362306a36Sopenharmony_ci			parent->sk_data_ready(parent);
23462306a36Sopenharmony_ci	} else {
23562306a36Sopenharmony_ci		/* Alas, it is possible again, because we do lookup
23662306a36Sopenharmony_ci		 * in main socket hash table and lock on listening
23762306a36Sopenharmony_ci		 * socket does not protect us more.
23862306a36Sopenharmony_ci		 */
23962306a36Sopenharmony_ci		__sk_add_backlog(child, skb);
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	bh_unlock_sock(child);
24362306a36Sopenharmony_ci	sock_put(child);
24462306a36Sopenharmony_ci	return ret;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_child_process);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_civoid dccp_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
25062306a36Sopenharmony_ci			 struct request_sock *rsk)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	DCCP_BUG("DCCP-ACK packets are never sent in LISTEN/RESPOND state");
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_reqsk_send_ack);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ciint dccp_reqsk_init(struct request_sock *req,
25862306a36Sopenharmony_ci		    struct dccp_sock const *dp, struct sk_buff const *skb)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct dccp_request_sock *dreq = dccp_rsk(req);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	spin_lock_init(&dreq->dreq_lock);
26362306a36Sopenharmony_ci	inet_rsk(req)->ir_rmt_port = dccp_hdr(skb)->dccph_sport;
26462306a36Sopenharmony_ci	inet_rsk(req)->ir_num	   = ntohs(dccp_hdr(skb)->dccph_dport);
26562306a36Sopenharmony_ci	inet_rsk(req)->acked	   = 0;
26662306a36Sopenharmony_ci	dreq->dreq_timestamp_echo  = 0;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/* inherit feature negotiation options from listening socket */
26962306a36Sopenharmony_ci	return dccp_feat_clone_list(&dp->dccps_featneg, &dreq->dreq_featneg);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_reqsk_init);
273