xref: /linux/net/dccp/ipv6.c (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23df80d93SArnaldo Carvalho de Melo /*
33df80d93SArnaldo Carvalho de Melo  *	DCCP over IPv6
43df80d93SArnaldo Carvalho de Melo  *	Linux INET6 implementation
53df80d93SArnaldo Carvalho de Melo  *
63df80d93SArnaldo Carvalho de Melo  *	Based on net/dccp6/ipv6.c
73df80d93SArnaldo Carvalho de Melo  *
83df80d93SArnaldo Carvalho de Melo  *	Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
93df80d93SArnaldo Carvalho de Melo  */
103df80d93SArnaldo Carvalho de Melo 
113df80d93SArnaldo Carvalho de Melo #include <linux/module.h>
123df80d93SArnaldo Carvalho de Melo #include <linux/random.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
143df80d93SArnaldo Carvalho de Melo #include <linux/xfrm.h>
15323fbd0eSAndrii #include <linux/string.h>
163df80d93SArnaldo Carvalho de Melo 
173df80d93SArnaldo Carvalho de Melo #include <net/addrconf.h>
183df80d93SArnaldo Carvalho de Melo #include <net/inet_common.h>
193df80d93SArnaldo Carvalho de Melo #include <net/inet_hashtables.h>
2014c85021SArnaldo Carvalho de Melo #include <net/inet_sock.h>
213df80d93SArnaldo Carvalho de Melo #include <net/inet6_connection_sock.h>
223df80d93SArnaldo Carvalho de Melo #include <net/inet6_hashtables.h>
233df80d93SArnaldo Carvalho de Melo #include <net/ip6_route.h>
243df80d93SArnaldo Carvalho de Melo #include <net/ipv6.h>
253df80d93SArnaldo Carvalho de Melo #include <net/protocol.h>
263df80d93SArnaldo Carvalho de Melo #include <net/transp_v6.h>
27aa0e4e4aSDavid S. Miller #include <net/ip6_checksum.h>
283df80d93SArnaldo Carvalho de Melo #include <net/xfrm.h>
296e5714eaSDavid S. Miller #include <net/secure_seq.h>
30b98b3304SFlorian Westphal #include <net/netns/generic.h>
31323fbd0eSAndrii #include <net/sock.h>
326be49deaSJason Xing #include <net/rstreason.h>
333df80d93SArnaldo Carvalho de Melo 
343df80d93SArnaldo Carvalho de Melo #include "dccp.h"
353df80d93SArnaldo Carvalho de Melo #include "ipv6.h"
364b79f0afSIan McDonald #include "feat.h"
373df80d93SArnaldo Carvalho de Melo 
38b98b3304SFlorian Westphal struct dccp_v6_pernet {
39b98b3304SFlorian Westphal 	struct sock *v6_ctl_sk;
40b98b3304SFlorian Westphal };
41b98b3304SFlorian Westphal 
42b98b3304SFlorian Westphal static unsigned int dccp_v6_pernet_id __read_mostly;
43b98b3304SFlorian Westphal 
44b98b3304SFlorian Westphal /* The per-net v6_ctl_sk is used for sending RSTs and ACKs */
4572478873SArnaldo Carvalho de Melo 
463b401a81SStephen Hemminger static const struct inet_connection_sock_af_ops dccp_ipv6_mapped;
473b401a81SStephen Hemminger static const struct inet_connection_sock_af_ops dccp_ipv6_af_ops;
483df80d93SArnaldo Carvalho de Melo 
496f4e5fffSGerrit Renker /* add pseudo-header to DCCP checksum stored in skb->csum */
50868c86bcSAl Viro static inline __sum16 dccp_v6_csum_finish(struct sk_buff *skb,
51b71d1d42SEric Dumazet 				      const struct in6_addr *saddr,
52b71d1d42SEric Dumazet 				      const struct in6_addr *daddr)
533df80d93SArnaldo Carvalho de Melo {
546f4e5fffSGerrit Renker 	return csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_DCCP, skb->csum);
556f4e5fffSGerrit Renker }
566f4e5fffSGerrit Renker 
57bb296246SHerbert Xu static inline void dccp_v6_send_check(struct sock *sk, struct sk_buff *skb)
586f4e5fffSGerrit Renker {
596f4e5fffSGerrit Renker 	struct ipv6_pinfo *np = inet6_sk(sk);
606f4e5fffSGerrit Renker 	struct dccp_hdr *dh = dccp_hdr(skb);
616f4e5fffSGerrit Renker 
626f4e5fffSGerrit Renker 	dccp_csum_outgoing(skb);
63efe4208fSEric Dumazet 	dh->dccph_checksum = dccp_v6_csum_finish(skb, &np->saddr, &sk->sk_v6_daddr);
643df80d93SArnaldo Carvalho de Melo }
653df80d93SArnaldo Carvalho de Melo 
666e5714eaSDavid S. Miller static inline __u64 dccp_v6_init_sequence(struct sk_buff *skb)
67d7f7365fSGerrit Renker {
680660e03fSArnaldo Carvalho de Melo 	return secure_dccpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32,
690660e03fSArnaldo Carvalho de Melo 					     ipv6_hdr(skb)->saddr.s6_addr32,
70865e9022SGerrit Renker 					     dccp_hdr(skb)->dccph_dport,
71865e9022SGerrit Renker 					     dccp_hdr(skb)->dccph_sport     );
72d7f7365fSGerrit Renker 
733df80d93SArnaldo Carvalho de Melo }
743df80d93SArnaldo Carvalho de Melo 
7532bbd879SStefano Brivio static int dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
76d5fdd6baSBrian Haley 			u8 type, u8 code, int offset, __be32 info)
773df80d93SArnaldo Carvalho de Melo {
78977ad86cSJann Horn 	const struct ipv6hdr *hdr;
791aa9d1a0SEric Dumazet 	const struct dccp_hdr *dh;
80e0bcfb0cSWei Yongjun 	struct dccp_sock *dp;
813df80d93SArnaldo Carvalho de Melo 	struct ipv6_pinfo *np;
823df80d93SArnaldo Carvalho de Melo 	struct sock *sk;
833df80d93SArnaldo Carvalho de Melo 	int err;
843df80d93SArnaldo Carvalho de Melo 	__u64 seq;
85ca12a1a4SPavel Emelyanov 	struct net *net = dev_net(skb->dev);
863df80d93SArnaldo Carvalho de Melo 
876af28974SEric Dumazet 	if (!pskb_may_pull(skb, offset + sizeof(*dh)))
886af28974SEric Dumazet 		return -EINVAL;
89977ad86cSJann Horn 	dh = (struct dccp_hdr *)(skb->data + offset);
90977ad86cSJann Horn 	if (!pskb_may_pull(skb, offset + __dccp_basic_hdr_len(dh)))
91977ad86cSJann Horn 		return -EINVAL;
92977ad86cSJann Horn 	hdr = (const struct ipv6hdr *)skb->data;
931aa9d1a0SEric Dumazet 	dh = (struct dccp_hdr *)(skb->data + offset);
94860239c5SWei Yongjun 
9552036a43SEric Dumazet 	sk = __inet6_lookup_established(net, &dccp_hashinfo,
96671a1c74SPavel Emelyanov 					&hdr->daddr, dh->dccph_dport,
9752036a43SEric Dumazet 					&hdr->saddr, ntohs(dh->dccph_sport),
984297a0efSDavid Ahern 					inet6_iif(skb), 0);
993df80d93SArnaldo Carvalho de Melo 
10052036a43SEric Dumazet 	if (!sk) {
101a16292a0SEric Dumazet 		__ICMP6_INC_STATS(net, __in6_dev_get(skb->dev),
102e41b5368SDenis V. Lunev 				  ICMP6_MIB_INERRORS);
10332bbd879SStefano Brivio 		return -ENOENT;
1043df80d93SArnaldo Carvalho de Melo 	}
1053df80d93SArnaldo Carvalho de Melo 
1063df80d93SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_TIME_WAIT) {
1079469c7b4SYOSHIFUJI Hideaki 		inet_twsk_put(inet_twsk(sk));
10832bbd879SStefano Brivio 		return 0;
1093df80d93SArnaldo Carvalho de Melo 	}
11052036a43SEric Dumazet 	seq = dccp_hdr_seq(dh);
11132bbd879SStefano Brivio 	if (sk->sk_state == DCCP_NEW_SYN_RECV) {
11232bbd879SStefano Brivio 		dccp_req_err(sk, seq);
11332bbd879SStefano Brivio 		return 0;
11432bbd879SStefano Brivio 	}
1153df80d93SArnaldo Carvalho de Melo 
1163df80d93SArnaldo Carvalho de Melo 	bh_lock_sock(sk);
1173df80d93SArnaldo Carvalho de Melo 	if (sock_owned_by_user(sk))
11802a1d6e7SEric Dumazet 		__NET_INC_STATS(net, LINUX_MIB_LOCKDROPPEDICMPS);
1193df80d93SArnaldo Carvalho de Melo 
1203df80d93SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_CLOSED)
1213df80d93SArnaldo Carvalho de Melo 		goto out;
1223df80d93SArnaldo Carvalho de Melo 
123e0bcfb0cSWei Yongjun 	dp = dccp_sk(sk);
124e0bcfb0cSWei Yongjun 	if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_LISTEN) &&
125e0bcfb0cSWei Yongjun 	    !between48(seq, dp->dccps_awl, dp->dccps_awh)) {
12602a1d6e7SEric Dumazet 		__NET_INC_STATS(net, LINUX_MIB_OUTOFWINDOWICMPS);
127e0bcfb0cSWei Yongjun 		goto out;
128e0bcfb0cSWei Yongjun 	}
129e0bcfb0cSWei Yongjun 
1303df80d93SArnaldo Carvalho de Melo 	np = inet6_sk(sk);
1313df80d93SArnaldo Carvalho de Melo 
132ec18d9a2SDavid S. Miller 	if (type == NDISC_REDIRECT) {
13345caeaa5SJon Maxwell 		if (!sock_owned_by_user(sk)) {
134ec18d9a2SDavid S. Miller 			struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
135ec18d9a2SDavid S. Miller 
1361ed5c48fSDavid S. Miller 			if (dst)
1376700c270SDavid S. Miller 				dst->ops->redirect(dst, sk, skb);
13845caeaa5SJon Maxwell 		}
139bd784a14SDuan Jiong 		goto out;
140ec18d9a2SDavid S. Miller 	}
141ec18d9a2SDavid S. Miller 
1423df80d93SArnaldo Carvalho de Melo 	if (type == ICMPV6_PKT_TOOBIG) {
1433df80d93SArnaldo Carvalho de Melo 		struct dst_entry *dst = NULL;
1443df80d93SArnaldo Carvalho de Melo 
14593b36cf3SHannes Frederic Sowa 		if (!ip6_sk_accept_pmtu(sk))
14693b36cf3SHannes Frederic Sowa 			goto out;
14793b36cf3SHannes Frederic Sowa 
1483df80d93SArnaldo Carvalho de Melo 		if (sock_owned_by_user(sk))
1493df80d93SArnaldo Carvalho de Melo 			goto out;
1503df80d93SArnaldo Carvalho de Melo 		if ((1 << sk->sk_state) & (DCCPF_LISTEN | DCCPF_CLOSED))
1513df80d93SArnaldo Carvalho de Melo 			goto out;
1523df80d93SArnaldo Carvalho de Melo 
15335ad9b9cSDavid S. Miller 		dst = inet6_csk_update_pmtu(sk, ntohl(info));
15435ad9b9cSDavid S. Miller 		if (!dst)
1553df80d93SArnaldo Carvalho de Melo 			goto out;
1563df80d93SArnaldo Carvalho de Melo 
15735ad9b9cSDavid S. Miller 		if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst))
1583df80d93SArnaldo Carvalho de Melo 			dccp_sync_mss(sk, dst_mtu(dst));
1593df80d93SArnaldo Carvalho de Melo 		goto out;
1603df80d93SArnaldo Carvalho de Melo 	}
1613df80d93SArnaldo Carvalho de Melo 
1623df80d93SArnaldo Carvalho de Melo 	icmpv6_err_convert(type, code, &err);
1633df80d93SArnaldo Carvalho de Melo 
1643df80d93SArnaldo Carvalho de Melo 	/* Might be for an request_sock */
1653df80d93SArnaldo Carvalho de Melo 	switch (sk->sk_state) {
1663df80d93SArnaldo Carvalho de Melo 	case DCCP_REQUESTING:
1673df80d93SArnaldo Carvalho de Melo 	case DCCP_RESPOND:  /* Cannot happen.
1683df80d93SArnaldo Carvalho de Melo 			       It can, it SYNs are crossed. --ANK */
1693df80d93SArnaldo Carvalho de Melo 		if (!sock_owned_by_user(sk)) {
170aa62d76bSEric Dumazet 			__DCCP_INC_STATS(DCCP_MIB_ATTEMPTFAILS);
1713df80d93SArnaldo Carvalho de Melo 			sk->sk_err = err;
1723df80d93SArnaldo Carvalho de Melo 			/*
1733df80d93SArnaldo Carvalho de Melo 			 * Wake people up to see the error
1743df80d93SArnaldo Carvalho de Melo 			 * (see connect in sock.c)
1753df80d93SArnaldo Carvalho de Melo 			 */
176e3ae2365SAlexander Aring 			sk_error_report(sk);
1773df80d93SArnaldo Carvalho de Melo 			dccp_done(sk);
1789a25f0cbSEric Dumazet 		} else {
1799a25f0cbSEric Dumazet 			WRITE_ONCE(sk->sk_err_soft, err);
1809a25f0cbSEric Dumazet 		}
1813df80d93SArnaldo Carvalho de Melo 		goto out;
1823df80d93SArnaldo Carvalho de Melo 	}
1833df80d93SArnaldo Carvalho de Melo 
1843fa29971SEric Dumazet 	if (!sock_owned_by_user(sk) && inet6_test_bit(RECVERR6, sk)) {
1853df80d93SArnaldo Carvalho de Melo 		sk->sk_err = err;
186e3ae2365SAlexander Aring 		sk_error_report(sk);
1879a25f0cbSEric Dumazet 	} else {
1889a25f0cbSEric Dumazet 		WRITE_ONCE(sk->sk_err_soft, err);
1899a25f0cbSEric Dumazet 	}
1903df80d93SArnaldo Carvalho de Melo out:
1913df80d93SArnaldo Carvalho de Melo 	bh_unlock_sock(sk);
1923df80d93SArnaldo Carvalho de Melo 	sock_put(sk);
19332bbd879SStefano Brivio 	return 0;
1943df80d93SArnaldo Carvalho de Melo }
1953df80d93SArnaldo Carvalho de Melo 
1963df80d93SArnaldo Carvalho de Melo 
197ea3bea3aSEric Dumazet static int dccp_v6_send_response(const struct sock *sk, struct request_sock *req)
1983df80d93SArnaldo Carvalho de Melo {
199634fb979SEric Dumazet 	struct inet_request_sock *ireq = inet_rsk(req);
2003df80d93SArnaldo Carvalho de Melo 	struct ipv6_pinfo *np = inet6_sk(sk);
2013df80d93SArnaldo Carvalho de Melo 	struct sk_buff *skb;
20220c59de2SArnaud Ebalard 	struct in6_addr *final_p, final;
2034c9483b2SDavid S. Miller 	struct flowi6 fl6;
2043df80d93SArnaldo Carvalho de Melo 	int err = -1;
205fd80eb94SDenis V. Lunev 	struct dst_entry *dst;
2063df80d93SArnaldo Carvalho de Melo 
2074c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
2084c9483b2SDavid S. Miller 	fl6.flowi6_proto = IPPROTO_DCCP;
209634fb979SEric Dumazet 	fl6.daddr = ireq->ir_v6_rmt_addr;
210634fb979SEric Dumazet 	fl6.saddr = ireq->ir_v6_loc_addr;
2114c9483b2SDavid S. Miller 	fl6.flowlabel = 0;
212634fb979SEric Dumazet 	fl6.flowi6_oif = ireq->ir_iif;
213634fb979SEric Dumazet 	fl6.fl6_dport = ireq->ir_rmt_port;
214b44084c2SEric Dumazet 	fl6.fl6_sport = htons(ireq->ir_num);
2153df98d79SPaul Moore 	security_req_classify_flow(req, flowi6_to_flowi_common(&fl6));
2163df80d93SArnaldo Carvalho de Melo 
2173df80d93SArnaldo Carvalho de Melo 
21845f6fad8SEric Dumazet 	rcu_read_lock();
21945f6fad8SEric Dumazet 	final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final);
22045f6fad8SEric Dumazet 	rcu_read_unlock();
2213df80d93SArnaldo Carvalho de Melo 
222c4e85f73SSabrina Dubroca 	dst = ip6_dst_lookup_flow(sock_net(sk), sk, &fl6, final_p);
22368d0c6d3SDavid S. Miller 	if (IS_ERR(dst)) {
22468d0c6d3SDavid S. Miller 		err = PTR_ERR(dst);
22568d0c6d3SDavid S. Miller 		dst = NULL;
2263df80d93SArnaldo Carvalho de Melo 		goto done;
22768d0c6d3SDavid S. Miller 	}
2283df80d93SArnaldo Carvalho de Melo 
2293df80d93SArnaldo Carvalho de Melo 	skb = dccp_make_response(sk, dst, req);
2303df80d93SArnaldo Carvalho de Melo 	if (skb != NULL) {
2313df80d93SArnaldo Carvalho de Melo 		struct dccp_hdr *dh = dccp_hdr(skb);
23256ac42bcSHuw Davies 		struct ipv6_txoptions *opt;
23345329e71SArnaldo Carvalho de Melo 
2346f4e5fffSGerrit Renker 		dh->dccph_checksum = dccp_v6_csum_finish(skb,
235634fb979SEric Dumazet 							 &ireq->ir_v6_loc_addr,
236634fb979SEric Dumazet 							 &ireq->ir_v6_rmt_addr);
237634fb979SEric Dumazet 		fl6.daddr = ireq->ir_v6_rmt_addr;
23845f6fad8SEric Dumazet 		rcu_read_lock();
23956ac42bcSHuw Davies 		opt = ireq->ipv6_opt;
24056ac42bcSHuw Davies 		if (!opt)
24156ac42bcSHuw Davies 			opt = rcu_dereference(np->opt);
2423c5b4d69SEric Dumazet 		err = ip6_xmit(sk, skb, &fl6, READ_ONCE(sk->sk_mark), opt,
24310bbf165SEric Dumazet 			       np->tclass, READ_ONCE(sk->sk_priority));
24445f6fad8SEric Dumazet 		rcu_read_unlock();
245b9df3cb8SGerrit Renker 		err = net_xmit_eval(err);
2463df80d93SArnaldo Carvalho de Melo 	}
2473df80d93SArnaldo Carvalho de Melo 
2483df80d93SArnaldo Carvalho de Melo done:
2490cbd7825SDavid S. Miller 	dst_release(dst);
2503df80d93SArnaldo Carvalho de Melo 	return err;
2513df80d93SArnaldo Carvalho de Melo }
2523df80d93SArnaldo Carvalho de Melo 
2533df80d93SArnaldo Carvalho de Melo static void dccp_v6_reqsk_destructor(struct request_sock *req)
2543df80d93SArnaldo Carvalho de Melo {
255d99a7bd2SGerrit Renker 	dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg);
25656ac42bcSHuw Davies 	kfree(inet_rsk(req)->ipv6_opt);
257634fb979SEric Dumazet 	kfree_skb(inet_rsk(req)->pktopts);
2583df80d93SArnaldo Carvalho de Melo }
2593df80d93SArnaldo Carvalho de Melo 
2606be49deaSJason Xing static void dccp_v6_ctl_send_reset(const struct sock *sk, struct sk_buff *rxskb,
2616be49deaSJason Xing 				   enum sk_rst_reason reason)
2623df80d93SArnaldo Carvalho de Melo {
263b71d1d42SEric Dumazet 	const struct ipv6hdr *rxip6h;
2643df80d93SArnaldo Carvalho de Melo 	struct sk_buff *skb;
2654c9483b2SDavid S. Miller 	struct flowi6 fl6;
266adf30907SEric Dumazet 	struct net *net = dev_net(skb_dst(rxskb)->dev);
267b98b3304SFlorian Westphal 	struct dccp_v6_pernet *pn;
268b98b3304SFlorian Westphal 	struct sock *ctl_sk;
269adf30907SEric Dumazet 	struct dst_entry *dst;
2703df80d93SArnaldo Carvalho de Melo 
271e356d37aSGerrit Renker 	if (dccp_hdr(rxskb)->dccph_type == DCCP_PKT_RESET)
2723df80d93SArnaldo Carvalho de Melo 		return;
2733df80d93SArnaldo Carvalho de Melo 
2743df80d93SArnaldo Carvalho de Melo 	if (!ipv6_unicast_destination(rxskb))
2753df80d93SArnaldo Carvalho de Melo 		return;
2763df80d93SArnaldo Carvalho de Melo 
277b98b3304SFlorian Westphal 	pn = net_generic(net, dccp_v6_pernet_id);
278b98b3304SFlorian Westphal 	ctl_sk = pn->v6_ctl_sk;
27902047741SPavel Emelyanov 	skb = dccp_ctl_make_reset(ctl_sk, rxskb);
2803df80d93SArnaldo Carvalho de Melo 	if (skb == NULL)
2813df80d93SArnaldo Carvalho de Melo 		return;
2823df80d93SArnaldo Carvalho de Melo 
2830660e03fSArnaldo Carvalho de Melo 	rxip6h = ipv6_hdr(rxskb);
284e356d37aSGerrit Renker 	dccp_hdr(skb)->dccph_checksum = dccp_v6_csum_finish(skb, &rxip6h->saddr,
2850660e03fSArnaldo Carvalho de Melo 							    &rxip6h->daddr);
2866f4e5fffSGerrit Renker 
2874c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
2884e3fd7a0SAlexey Dobriyan 	fl6.daddr = rxip6h->saddr;
2894e3fd7a0SAlexey Dobriyan 	fl6.saddr = rxip6h->daddr;
2906f4e5fffSGerrit Renker 
2914c9483b2SDavid S. Miller 	fl6.flowi6_proto = IPPROTO_DCCP;
2924c9483b2SDavid S. Miller 	fl6.flowi6_oif = inet6_iif(rxskb);
2931958b856SDavid S. Miller 	fl6.fl6_dport = dccp_hdr(skb)->dccph_dport;
2941958b856SDavid S. Miller 	fl6.fl6_sport = dccp_hdr(skb)->dccph_sport;
2953df98d79SPaul Moore 	security_skb_classify_flow(rxskb, flowi6_to_flowi_common(&fl6));
2963df80d93SArnaldo Carvalho de Melo 
2973df80d93SArnaldo Carvalho de Melo 	/* sk = NULL, but it is safe for now. RST socket required. */
298c4e85f73SSabrina Dubroca 	dst = ip6_dst_lookup_flow(sock_net(ctl_sk), ctl_sk, &fl6, NULL);
29968d0c6d3SDavid S. Miller 	if (!IS_ERR(dst)) {
300adf30907SEric Dumazet 		skb_dst_set(skb, dst);
3014f6570d7SEric Dumazet 		ip6_xmit(ctl_sk, skb, &fl6, 0, NULL, 0, 0);
3027309f882SEric Dumazet 		DCCP_INC_STATS(DCCP_MIB_OUTSEGS);
3037309f882SEric Dumazet 		DCCP_INC_STATS(DCCP_MIB_OUTRSTS);
3043df80d93SArnaldo Carvalho de Melo 		return;
3053df80d93SArnaldo Carvalho de Melo 	}
3063df80d93SArnaldo Carvalho de Melo 
3073df80d93SArnaldo Carvalho de Melo 	kfree_skb(skb);
3083df80d93SArnaldo Carvalho de Melo }
3093df80d93SArnaldo Carvalho de Melo 
31073c9e02cSGerrit Renker static struct request_sock_ops dccp6_request_sock_ops = {
31173c9e02cSGerrit Renker 	.family		= AF_INET6,
31273c9e02cSGerrit Renker 	.obj_size	= sizeof(struct dccp6_request_sock),
31373c9e02cSGerrit Renker 	.rtx_syn_ack	= dccp_v6_send_response,
31473c9e02cSGerrit Renker 	.send_ack	= dccp_reqsk_send_ack,
31573c9e02cSGerrit Renker 	.destructor	= dccp_v6_reqsk_destructor,
31673c9e02cSGerrit Renker 	.send_reset	= dccp_v6_ctl_send_reset,
317c72e1183SEric Dumazet 	.syn_ack_timeout = dccp_syn_ack_timeout,
31873c9e02cSGerrit Renker };
31973c9e02cSGerrit Renker 
3203df80d93SArnaldo Carvalho de Melo static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
3213df80d93SArnaldo Carvalho de Melo {
3223df80d93SArnaldo Carvalho de Melo 	struct request_sock *req;
3233df80d93SArnaldo Carvalho de Melo 	struct dccp_request_sock *dreq;
324634fb979SEric Dumazet 	struct inet_request_sock *ireq;
3253df80d93SArnaldo Carvalho de Melo 	struct ipv6_pinfo *np = inet6_sk(sk);
32660fe62e7SAndrea Bittau 	const __be32 service = dccp_hdr_request(skb)->dccph_req_service;
3273df80d93SArnaldo Carvalho de Melo 	struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
3283df80d93SArnaldo Carvalho de Melo 
3293df80d93SArnaldo Carvalho de Melo 	if (skb->protocol == htons(ETH_P_IP))
3303df80d93SArnaldo Carvalho de Melo 		return dccp_v4_conn_request(sk, skb);
3313df80d93SArnaldo Carvalho de Melo 
3323df80d93SArnaldo Carvalho de Melo 	if (!ipv6_unicast_destination(skb))
3334a5409a5SGerrit Renker 		return 0;	/* discard, don't send a reset here */
3343df80d93SArnaldo Carvalho de Melo 
335dcc32f4fSJakub Kicinski 	if (ipv6_addr_v4mapped(&ipv6_hdr(skb)->saddr)) {
336dcc32f4fSJakub Kicinski 		__IP6_INC_STATS(sock_net(sk), NULL, IPSTATS_MIB_INHDRERRORS);
337dcc32f4fSJakub Kicinski 		return 0;
338dcc32f4fSJakub Kicinski 	}
339dcc32f4fSJakub Kicinski 
3403df80d93SArnaldo Carvalho de Melo 	if (dccp_bad_service_code(sk, service)) {
3414a5409a5SGerrit Renker 		dcb->dccpd_reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE;
3423df80d93SArnaldo Carvalho de Melo 		goto drop;
3433df80d93SArnaldo Carvalho de Melo 	}
3443df80d93SArnaldo Carvalho de Melo 	/*
3453df80d93SArnaldo Carvalho de Melo 	 * There are no SYN attacks on IPv6, yet...
3463df80d93SArnaldo Carvalho de Melo 	 */
3474a5409a5SGerrit Renker 	dcb->dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY;
3483df80d93SArnaldo Carvalho de Melo 	if (inet_csk_reqsk_queue_is_full(sk))
3493df80d93SArnaldo Carvalho de Melo 		goto drop;
3503df80d93SArnaldo Carvalho de Melo 
3515ea8ea2cSEric Dumazet 	if (sk_acceptq_is_full(sk))
3523df80d93SArnaldo Carvalho de Melo 		goto drop;
3533df80d93SArnaldo Carvalho de Melo 
354a1a5344dSEric Dumazet 	req = inet_reqsk_alloc(&dccp6_request_sock_ops, sk, true);
3553df80d93SArnaldo Carvalho de Melo 	if (req == NULL)
3563df80d93SArnaldo Carvalho de Melo 		goto drop;
3573df80d93SArnaldo Carvalho de Melo 
358ac75773cSGerrit Renker 	if (dccp_reqsk_init(req, dccp_sk(sk), skb))
359ac75773cSGerrit Renker 		goto drop_and_free;
3603df80d93SArnaldo Carvalho de Melo 
3618b819412SGerrit Renker 	dreq = dccp_rsk(req);
3628b819412SGerrit Renker 	if (dccp_parse_options(sk, dreq, skb))
3638b819412SGerrit Renker 		goto drop_and_free;
3648b819412SGerrit Renker 
365634fb979SEric Dumazet 	ireq = inet_rsk(req);
366634fb979SEric Dumazet 	ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr;
367634fb979SEric Dumazet 	ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr;
3683f66b083SEric Dumazet 	ireq->ireq_family = AF_INET6;
369b855ff82SEric Dumazet 	ireq->ir_mark = inet_request_mark(sk, skb);
3703df80d93SArnaldo Carvalho de Melo 
37123be1e0eSKuniyuki Iwashima 	if (security_inet_conn_request(sk, skb, req))
37223be1e0eSKuniyuki Iwashima 		goto drop_and_free;
37323be1e0eSKuniyuki Iwashima 
374a224772dSEric Dumazet 	if (ipv6_opt_accepted(sk, skb, IP6CB(skb)) ||
3753df80d93SArnaldo Carvalho de Melo 	    np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
3763df80d93SArnaldo Carvalho de Melo 	    np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
37763354797SReshetova, Elena 		refcount_inc(&skb->users);
378634fb979SEric Dumazet 		ireq->pktopts = skb;
3793df80d93SArnaldo Carvalho de Melo 	}
38036f7cec4SEric Dumazet 	ireq->ir_iif = READ_ONCE(sk->sk_bound_dev_if);
3813df80d93SArnaldo Carvalho de Melo 
3823df80d93SArnaldo Carvalho de Melo 	/* So that link locals have meaning */
38336f7cec4SEric Dumazet 	if (!ireq->ir_iif &&
384634fb979SEric Dumazet 	    ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
385634fb979SEric Dumazet 		ireq->ir_iif = inet6_iif(skb);
3863df80d93SArnaldo Carvalho de Melo 
3873df80d93SArnaldo Carvalho de Melo 	/*
3883df80d93SArnaldo Carvalho de Melo 	 * Step 3: Process LISTEN state
3893df80d93SArnaldo Carvalho de Melo 	 *
3903df80d93SArnaldo Carvalho de Melo 	 *   Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie
3913df80d93SArnaldo Carvalho de Melo 	 *
392f541fb7eSSamuel Jero 	 * Setting S.SWL/S.SWH to is deferred to dccp_create_openreq_child().
3933df80d93SArnaldo Carvalho de Melo 	 */
3943df80d93SArnaldo Carvalho de Melo 	dreq->dreq_isr	   = dcb->dccpd_seq;
395f541fb7eSSamuel Jero 	dreq->dreq_gsr     = dreq->dreq_isr;
396865e9022SGerrit Renker 	dreq->dreq_iss	   = dccp_v6_init_sequence(skb);
397f541fb7eSSamuel Jero 	dreq->dreq_gss     = dreq->dreq_iss;
3983df80d93SArnaldo Carvalho de Melo 	dreq->dreq_service = service;
3993df80d93SArnaldo Carvalho de Melo 
4001a2c6181SChristoph Paasch 	if (dccp_v6_send_response(sk, req))
4013df80d93SArnaldo Carvalho de Melo 		goto drop_and_free;
4023df80d93SArnaldo Carvalho de Melo 
403*ff46e3b4Sluoxuanqiang 	if (unlikely(!inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT)))
404*ff46e3b4Sluoxuanqiang 		reqsk_free(req);
405*ff46e3b4Sluoxuanqiang 	else
4060c2232b0SXin Long 		reqsk_put(req);
407*ff46e3b4Sluoxuanqiang 
4083df80d93SArnaldo Carvalho de Melo 	return 0;
4093df80d93SArnaldo Carvalho de Melo 
4103df80d93SArnaldo Carvalho de Melo drop_and_free:
4113df80d93SArnaldo Carvalho de Melo 	reqsk_free(req);
4123df80d93SArnaldo Carvalho de Melo drop:
413aa62d76bSEric Dumazet 	__DCCP_INC_STATS(DCCP_MIB_ATTEMPTFAILS);
4143df80d93SArnaldo Carvalho de Melo 	return -1;
4153df80d93SArnaldo Carvalho de Melo }
4163df80d93SArnaldo Carvalho de Melo 
4170c27171eSEric Dumazet static struct sock *dccp_v6_request_recv_sock(const struct sock *sk,
4183df80d93SArnaldo Carvalho de Melo 					      struct sk_buff *skb,
4193df80d93SArnaldo Carvalho de Melo 					      struct request_sock *req,
4205e0724d0SEric Dumazet 					      struct dst_entry *dst,
4215e0724d0SEric Dumazet 					      struct request_sock *req_unhash,
4225e0724d0SEric Dumazet 					      bool *own_req)
4233df80d93SArnaldo Carvalho de Melo {
424634fb979SEric Dumazet 	struct inet_request_sock *ireq = inet_rsk(req);
4250c27171eSEric Dumazet 	struct ipv6_pinfo *newnp;
4260c27171eSEric Dumazet 	const struct ipv6_pinfo *np = inet6_sk(sk);
42745f6fad8SEric Dumazet 	struct ipv6_txoptions *opt;
4283df80d93SArnaldo Carvalho de Melo 	struct inet_sock *newinet;
4293df80d93SArnaldo Carvalho de Melo 	struct dccp6_sock *newdp6;
4303df80d93SArnaldo Carvalho de Melo 	struct sock *newsk;
4313df80d93SArnaldo Carvalho de Melo 
4323df80d93SArnaldo Carvalho de Melo 	if (skb->protocol == htons(ETH_P_IP)) {
4333df80d93SArnaldo Carvalho de Melo 		/*
4343df80d93SArnaldo Carvalho de Melo 		 *	v6 mapped
4353df80d93SArnaldo Carvalho de Melo 		 */
4365e0724d0SEric Dumazet 		newsk = dccp_v4_request_recv_sock(sk, skb, req, dst,
4375e0724d0SEric Dumazet 						  req_unhash, own_req);
4383df80d93SArnaldo Carvalho de Melo 		if (newsk == NULL)
4393df80d93SArnaldo Carvalho de Melo 			return NULL;
4403df80d93SArnaldo Carvalho de Melo 
4413df80d93SArnaldo Carvalho de Melo 		newdp6 = (struct dccp6_sock *)newsk;
4423df80d93SArnaldo Carvalho de Melo 		newinet = inet_sk(newsk);
4433df80d93SArnaldo Carvalho de Melo 		newinet->pinet6 = &newdp6->inet6;
4443df80d93SArnaldo Carvalho de Melo 		newnp = inet6_sk(newsk);
4453df80d93SArnaldo Carvalho de Melo 
4463df80d93SArnaldo Carvalho de Melo 		memcpy(newnp, np, sizeof(struct ipv6_pinfo));
4473df80d93SArnaldo Carvalho de Melo 
448d1e559d0SEric Dumazet 		newnp->saddr = newsk->sk_v6_rcv_saddr;
4493df80d93SArnaldo Carvalho de Melo 
4503df80d93SArnaldo Carvalho de Melo 		inet_csk(newsk)->icsk_af_ops = &dccp_ipv6_mapped;
4513df80d93SArnaldo Carvalho de Melo 		newsk->sk_backlog_rcv = dccp_v4_do_rcv;
4523df80d93SArnaldo Carvalho de Melo 		newnp->pktoptions  = NULL;
4533df80d93SArnaldo Carvalho de Melo 		newnp->opt	   = NULL;
45483eaddabSWANG Cong 		newnp->ipv6_mc_list = NULL;
45583eaddabSWANG Cong 		newnp->ipv6_ac_list = NULL;
45683eaddabSWANG Cong 		newnp->ipv6_fl_list = NULL;
457e0aa6770SEric Dumazet 		newnp->mcast_oif   = inet_iif(skb);
458e0aa6770SEric Dumazet 		newnp->mcast_hops  = ip_hdr(skb)->ttl;
4593df80d93SArnaldo Carvalho de Melo 
4603df80d93SArnaldo Carvalho de Melo 		/*
4613df80d93SArnaldo Carvalho de Melo 		 * No need to charge this sock to the relevant IPv6 refcnt debug socks count
4623df80d93SArnaldo Carvalho de Melo 		 * here, dccp_create_openreq_child now does this for us, see the comment in
4633df80d93SArnaldo Carvalho de Melo 		 * that function for the gory details. -acme
4643df80d93SArnaldo Carvalho de Melo 		 */
4653df80d93SArnaldo Carvalho de Melo 
4663df80d93SArnaldo Carvalho de Melo 		/* It is tricky place. Until this moment IPv4 tcp
4673df80d93SArnaldo Carvalho de Melo 		   worked with IPv6 icsk.icsk_af_ops.
4683df80d93SArnaldo Carvalho de Melo 		   Sync it now.
4693df80d93SArnaldo Carvalho de Melo 		 */
470d83d8461SArnaldo Carvalho de Melo 		dccp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
4713df80d93SArnaldo Carvalho de Melo 
4723df80d93SArnaldo Carvalho de Melo 		return newsk;
4733df80d93SArnaldo Carvalho de Melo 	}
4743df80d93SArnaldo Carvalho de Melo 
4753df80d93SArnaldo Carvalho de Melo 
4763df80d93SArnaldo Carvalho de Melo 	if (sk_acceptq_is_full(sk))
4773df80d93SArnaldo Carvalho de Melo 		goto out_overflow;
4783df80d93SArnaldo Carvalho de Melo 
479f76b33c3SEric Dumazet 	if (!dst) {
4804c9483b2SDavid S. Miller 		struct flowi6 fl6;
4813df80d93SArnaldo Carvalho de Melo 
482f76b33c3SEric Dumazet 		dst = inet6_csk_route_req(sk, &fl6, req, IPPROTO_DCCP);
483f76b33c3SEric Dumazet 		if (!dst)
4843df80d93SArnaldo Carvalho de Melo 			goto out;
4853df80d93SArnaldo Carvalho de Melo 	}
4863df80d93SArnaldo Carvalho de Melo 
4873df80d93SArnaldo Carvalho de Melo 	newsk = dccp_create_openreq_child(sk, req, skb);
4883df80d93SArnaldo Carvalho de Melo 	if (newsk == NULL)
489093d2823SBalazs Scheidler 		goto out_nonewsk;
4903df80d93SArnaldo Carvalho de Melo 
4913df80d93SArnaldo Carvalho de Melo 	/*
4923df80d93SArnaldo Carvalho de Melo 	 * No need to charge this sock to the relevant IPv6 refcnt debug socks
4933df80d93SArnaldo Carvalho de Melo 	 * count here, dccp_create_openreq_child now does this for us, see the
4943df80d93SArnaldo Carvalho de Melo 	 * comment in that function for the gory details. -acme
4953df80d93SArnaldo Carvalho de Melo 	 */
4963df80d93SArnaldo Carvalho de Melo 
4976bd4f355SEric Dumazet 	ip6_dst_store(newsk, dst, NULL, NULL);
49845329e71SArnaldo Carvalho de Melo 	newsk->sk_route_caps = dst->dev->features & ~(NETIF_F_IP_CSUM |
49945329e71SArnaldo Carvalho de Melo 						      NETIF_F_TSO);
5003df80d93SArnaldo Carvalho de Melo 	newdp6 = (struct dccp6_sock *)newsk;
5013df80d93SArnaldo Carvalho de Melo 	newinet = inet_sk(newsk);
5023df80d93SArnaldo Carvalho de Melo 	newinet->pinet6 = &newdp6->inet6;
5033df80d93SArnaldo Carvalho de Melo 	newnp = inet6_sk(newsk);
5043df80d93SArnaldo Carvalho de Melo 
5053df80d93SArnaldo Carvalho de Melo 	memcpy(newnp, np, sizeof(struct ipv6_pinfo));
5063df80d93SArnaldo Carvalho de Melo 
507634fb979SEric Dumazet 	newsk->sk_v6_daddr	= ireq->ir_v6_rmt_addr;
508634fb979SEric Dumazet 	newnp->saddr		= ireq->ir_v6_loc_addr;
509634fb979SEric Dumazet 	newsk->sk_v6_rcv_saddr	= ireq->ir_v6_loc_addr;
510634fb979SEric Dumazet 	newsk->sk_bound_dev_if	= ireq->ir_iif;
5113df80d93SArnaldo Carvalho de Melo 
5123df80d93SArnaldo Carvalho de Melo 	/* Now IPv6 options...
5133df80d93SArnaldo Carvalho de Melo 
5143df80d93SArnaldo Carvalho de Melo 	   First: no IPv4 options.
5153df80d93SArnaldo Carvalho de Melo 	 */
516f6d8bd05SEric Dumazet 	newinet->inet_opt = NULL;
5173df80d93SArnaldo Carvalho de Melo 
5183df80d93SArnaldo Carvalho de Melo 	/* Clone RX bits */
5193df80d93SArnaldo Carvalho de Melo 	newnp->rxopt.all = np->rxopt.all;
5203df80d93SArnaldo Carvalho de Melo 
52183eaddabSWANG Cong 	newnp->ipv6_mc_list = NULL;
52283eaddabSWANG Cong 	newnp->ipv6_ac_list = NULL;
52383eaddabSWANG Cong 	newnp->ipv6_fl_list = NULL;
5243df80d93SArnaldo Carvalho de Melo 	newnp->pktoptions = NULL;
5253df80d93SArnaldo Carvalho de Melo 	newnp->opt	  = NULL;
5263df80d93SArnaldo Carvalho de Melo 	newnp->mcast_oif  = inet6_iif(skb);
5270660e03fSArnaldo Carvalho de Melo 	newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
5283df80d93SArnaldo Carvalho de Melo 
52945329e71SArnaldo Carvalho de Melo 	/*
53045329e71SArnaldo Carvalho de Melo 	 * Clone native IPv6 options from listening socket (if any)
53145329e71SArnaldo Carvalho de Melo 	 *
53245329e71SArnaldo Carvalho de Melo 	 * Yes, keeping reference count would be much more clever, but we make
53345329e71SArnaldo Carvalho de Melo 	 * one more one thing there: reattach optmem to newsk.
5343df80d93SArnaldo Carvalho de Melo 	 */
53556ac42bcSHuw Davies 	opt = ireq->ipv6_opt;
53656ac42bcSHuw Davies 	if (!opt)
53745f6fad8SEric Dumazet 		opt = rcu_dereference(np->opt);
53845f6fad8SEric Dumazet 	if (opt) {
53945f6fad8SEric Dumazet 		opt = ipv6_dup_options(newsk, opt);
54045f6fad8SEric Dumazet 		RCU_INIT_POINTER(newnp->opt, opt);
54145f6fad8SEric Dumazet 	}
542d83d8461SArnaldo Carvalho de Melo 	inet_csk(newsk)->icsk_ext_hdr_len = 0;
54345f6fad8SEric Dumazet 	if (opt)
54445f6fad8SEric Dumazet 		inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen +
54545f6fad8SEric Dumazet 						    opt->opt_flen;
5463df80d93SArnaldo Carvalho de Melo 
5473df80d93SArnaldo Carvalho de Melo 	dccp_sync_mss(newsk, dst_mtu(dst));
5483df80d93SArnaldo Carvalho de Melo 
549c720c7e8SEric Dumazet 	newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6;
550c720c7e8SEric Dumazet 	newinet->inet_rcv_saddr = LOOPBACK4_IPV6;
5513df80d93SArnaldo Carvalho de Melo 
552093d2823SBalazs Scheidler 	if (__inet_inherit_port(sk, newsk) < 0) {
553e337e24dSChristoph Paasch 		inet_csk_prepare_forced_close(newsk);
554e337e24dSChristoph Paasch 		dccp_done(newsk);
555093d2823SBalazs Scheidler 		goto out;
556093d2823SBalazs Scheidler 	}
55701770a16SRicardo Dias 	*own_req = inet_ehash_nolisten(newsk, req_to_sk(req_unhash), NULL);
558ce105008SEric Dumazet 	/* Clone pktoptions received with SYN, if we own the req */
559ce105008SEric Dumazet 	if (*own_req && ireq->pktopts) {
560ca43ccf4SKuniyuki Iwashima 		newnp->pktoptions = skb_clone_and_charge_r(ireq->pktopts, newsk);
561ce105008SEric Dumazet 		consume_skb(ireq->pktopts);
562ce105008SEric Dumazet 		ireq->pktopts = NULL;
563ce105008SEric Dumazet 	}
5643df80d93SArnaldo Carvalho de Melo 
5653df80d93SArnaldo Carvalho de Melo 	return newsk;
5663df80d93SArnaldo Carvalho de Melo 
5673df80d93SArnaldo Carvalho de Melo out_overflow:
56802a1d6e7SEric Dumazet 	__NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
569093d2823SBalazs Scheidler out_nonewsk:
570093d2823SBalazs Scheidler 	dst_release(dst);
5713df80d93SArnaldo Carvalho de Melo out:
57202a1d6e7SEric Dumazet 	__NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENDROPS);
5733df80d93SArnaldo Carvalho de Melo 	return NULL;
5743df80d93SArnaldo Carvalho de Melo }
5753df80d93SArnaldo Carvalho de Melo 
5763df80d93SArnaldo Carvalho de Melo /* The socket must have it's spinlock held when we get
5773df80d93SArnaldo Carvalho de Melo  * here.
5783df80d93SArnaldo Carvalho de Melo  *
5793df80d93SArnaldo Carvalho de Melo  * We have a potential double-lock case here, so even when
5803df80d93SArnaldo Carvalho de Melo  * doing backlog processing we use the BH locking scheme.
5813df80d93SArnaldo Carvalho de Melo  * This is because we cannot sleep with the original spinlock
5823df80d93SArnaldo Carvalho de Melo  * held.
5833df80d93SArnaldo Carvalho de Melo  */
5843df80d93SArnaldo Carvalho de Melo static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
5853df80d93SArnaldo Carvalho de Melo {
5863df80d93SArnaldo Carvalho de Melo 	struct ipv6_pinfo *np = inet6_sk(sk);
5873df80d93SArnaldo Carvalho de Melo 	struct sk_buff *opt_skb = NULL;
5883df80d93SArnaldo Carvalho de Melo 
5893df80d93SArnaldo Carvalho de Melo 	/* Imagine: socket is IPv6. IPv4 packet arrives,
5903df80d93SArnaldo Carvalho de Melo 	   goes to IPv4 receive handler and backlogged.
5913df80d93SArnaldo Carvalho de Melo 	   From backlog it always goes here. Kerboom...
5923df80d93SArnaldo Carvalho de Melo 	   Fortunately, dccp_rcv_established and rcv_established
5933df80d93SArnaldo Carvalho de Melo 	   handle them correctly, but it is not case with
5943df80d93SArnaldo Carvalho de Melo 	   dccp_v6_hnd_req and dccp_v6_ctl_send_reset().   --ANK
5953df80d93SArnaldo Carvalho de Melo 	 */
5963df80d93SArnaldo Carvalho de Melo 
5973df80d93SArnaldo Carvalho de Melo 	if (skb->protocol == htons(ETH_P_IP))
5983df80d93SArnaldo Carvalho de Melo 		return dccp_v4_do_rcv(sk, skb);
5993df80d93SArnaldo Carvalho de Melo 
600fda9ef5dSDmitry Mishin 	if (sk_filter(sk, skb))
6013df80d93SArnaldo Carvalho de Melo 		goto discard;
6023df80d93SArnaldo Carvalho de Melo 
6033df80d93SArnaldo Carvalho de Melo 	/*
60445329e71SArnaldo Carvalho de Melo 	 * socket locking is here for SMP purposes as backlog rcv is currently
60545329e71SArnaldo Carvalho de Melo 	 * called with bh processing disabled.
6063df80d93SArnaldo Carvalho de Melo 	 */
6073df80d93SArnaldo Carvalho de Melo 
6083df80d93SArnaldo Carvalho de Melo 	/* Do Stevens' IPV6_PKTOPTIONS.
6093df80d93SArnaldo Carvalho de Melo 
6103df80d93SArnaldo Carvalho de Melo 	   Yes, guys, it is the only place in our code, where we
6113df80d93SArnaldo Carvalho de Melo 	   may make it not affecting IPv4.
6123df80d93SArnaldo Carvalho de Melo 	   The rest of code is protocol independent,
6133df80d93SArnaldo Carvalho de Melo 	   and I do not like idea to uglify IPv4.
6143df80d93SArnaldo Carvalho de Melo 
6153df80d93SArnaldo Carvalho de Melo 	   Actually, all the idea behind IPV6_PKTOPTIONS
6163df80d93SArnaldo Carvalho de Melo 	   looks not very well thought. For now we latch
6173df80d93SArnaldo Carvalho de Melo 	   options, received in the last packet, enqueued
6183df80d93SArnaldo Carvalho de Melo 	   by tcp. Feel free to propose better solution.
6193df80d93SArnaldo Carvalho de Melo 					       --ANK (980728)
6203df80d93SArnaldo Carvalho de Melo 	 */
6213df80d93SArnaldo Carvalho de Melo 	if (np->rxopt.all)
622ca43ccf4SKuniyuki Iwashima 		opt_skb = skb_clone_and_charge_r(skb, sk);
6233df80d93SArnaldo Carvalho de Melo 
6243df80d93SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_OPEN) { /* Fast path */
6253df80d93SArnaldo Carvalho de Melo 		if (dccp_rcv_established(sk, skb, dccp_hdr(skb), skb->len))
6263df80d93SArnaldo Carvalho de Melo 			goto reset;
627323fbd0eSAndrii 		if (opt_skb)
628323fbd0eSAndrii 			goto ipv6_pktoptions;
6293df80d93SArnaldo Carvalho de Melo 		return 0;
6303df80d93SArnaldo Carvalho de Melo 	}
6313df80d93SArnaldo Carvalho de Melo 
632d83ca5acSGerrit Renker 	/*
633d83ca5acSGerrit Renker 	 *  Step 3: Process LISTEN state
634d83ca5acSGerrit Renker 	 *     If S.state == LISTEN,
635d83ca5acSGerrit Renker 	 *	 If P.type == Request or P contains a valid Init Cookie option,
636d83ca5acSGerrit Renker 	 *	      (* Must scan the packet's options to check for Init
637d83ca5acSGerrit Renker 	 *		 Cookies.  Only Init Cookies are processed here,
638d83ca5acSGerrit Renker 	 *		 however; other options are processed in Step 8.  This
639d83ca5acSGerrit Renker 	 *		 scan need only be performed if the endpoint uses Init
640d83ca5acSGerrit Renker 	 *		 Cookies *)
641d83ca5acSGerrit Renker 	 *	      (* Generate a new socket and switch to that socket *)
642d83ca5acSGerrit Renker 	 *	      Set S := new socket for this port pair
643d83ca5acSGerrit Renker 	 *	      S.state = RESPOND
644d83ca5acSGerrit Renker 	 *	      Choose S.ISS (initial seqno) or set from Init Cookies
645d83ca5acSGerrit Renker 	 *	      Initialize S.GAR := S.ISS
646d83ca5acSGerrit Renker 	 *	      Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookies
647d83ca5acSGerrit Renker 	 *	      Continue with S.state == RESPOND
648d83ca5acSGerrit Renker 	 *	      (* A Response packet will be generated in Step 11 *)
649d83ca5acSGerrit Renker 	 *	 Otherwise,
650d83ca5acSGerrit Renker 	 *	      Generate Reset(No Connection) unless P.type == Reset
651d83ca5acSGerrit Renker 	 *	      Drop packet and return
652d83ca5acSGerrit Renker 	 *
653d83ca5acSGerrit Renker 	 * NOTE: the check for the packet types is done in
654d83ca5acSGerrit Renker 	 *	 dccp_rcv_state_process
655d83ca5acSGerrit Renker 	 */
6563df80d93SArnaldo Carvalho de Melo 
6573df80d93SArnaldo Carvalho de Melo 	if (dccp_rcv_state_process(sk, skb, dccp_hdr(skb), skb->len))
6583df80d93SArnaldo Carvalho de Melo 		goto reset;
659323fbd0eSAndrii 	if (opt_skb)
660323fbd0eSAndrii 		goto ipv6_pktoptions;
6613df80d93SArnaldo Carvalho de Melo 	return 0;
6623df80d93SArnaldo Carvalho de Melo 
6633df80d93SArnaldo Carvalho de Melo reset:
6646be49deaSJason Xing 	dccp_v6_ctl_send_reset(sk, skb, SK_RST_REASON_NOT_SPECIFIED);
6653df80d93SArnaldo Carvalho de Melo discard:
66645329e71SArnaldo Carvalho de Melo 	if (opt_skb != NULL)
6673df80d93SArnaldo Carvalho de Melo 		__kfree_skb(opt_skb);
6683df80d93SArnaldo Carvalho de Melo 	kfree_skb(skb);
6693df80d93SArnaldo Carvalho de Melo 	return 0;
670323fbd0eSAndrii 
671323fbd0eSAndrii /* Handling IPV6_PKTOPTIONS skb the similar
672323fbd0eSAndrii  * way it's done for net/ipv6/tcp_ipv6.c
673323fbd0eSAndrii  */
674323fbd0eSAndrii ipv6_pktoptions:
675323fbd0eSAndrii 	if (!((1 << sk->sk_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) {
676323fbd0eSAndrii 		if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo)
677d2f011a0SEric Dumazet 			WRITE_ONCE(np->mcast_oif, inet6_iif(opt_skb));
678323fbd0eSAndrii 		if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)
6792da23eb0SEric Dumazet 			WRITE_ONCE(np->mcast_hops, ipv6_hdr(opt_skb)->hop_limit);
680323fbd0eSAndrii 		if (np->rxopt.bits.rxflow || np->rxopt.bits.rxtclass)
681323fbd0eSAndrii 			np->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(opt_skb));
6823cccda8dSEric Dumazet 		if (inet6_test_bit(REPFLOW, sk))
683323fbd0eSAndrii 			np->flow_label = ip6_flowlabel(ipv6_hdr(opt_skb));
684323fbd0eSAndrii 		if (ipv6_opt_accepted(sk, opt_skb,
685323fbd0eSAndrii 				      &DCCP_SKB_CB(opt_skb)->header.h6)) {
686323fbd0eSAndrii 			memmove(IP6CB(opt_skb),
687323fbd0eSAndrii 				&DCCP_SKB_CB(opt_skb)->header.h6,
688323fbd0eSAndrii 				sizeof(struct inet6_skb_parm));
689323fbd0eSAndrii 			opt_skb = xchg(&np->pktoptions, opt_skb);
690323fbd0eSAndrii 		} else {
691323fbd0eSAndrii 			__kfree_skb(opt_skb);
692323fbd0eSAndrii 			opt_skb = xchg(&np->pktoptions, NULL);
693323fbd0eSAndrii 		}
694323fbd0eSAndrii 	}
695323fbd0eSAndrii 
696323fbd0eSAndrii 	kfree_skb(opt_skb);
697323fbd0eSAndrii 	return 0;
6983df80d93SArnaldo Carvalho de Melo }
6993df80d93SArnaldo Carvalho de Melo 
700e5bbef20SHerbert Xu static int dccp_v6_rcv(struct sk_buff *skb)
7013df80d93SArnaldo Carvalho de Melo {
7023df80d93SArnaldo Carvalho de Melo 	const struct dccp_hdr *dh;
7033b24d854SEric Dumazet 	bool refcounted;
7043df80d93SArnaldo Carvalho de Melo 	struct sock *sk;
7056f4e5fffSGerrit Renker 	int min_cov;
7063df80d93SArnaldo Carvalho de Melo 
7076f4e5fffSGerrit Renker 	/* Step 1: Check header basics */
7083df80d93SArnaldo Carvalho de Melo 
7093df80d93SArnaldo Carvalho de Melo 	if (dccp_invalid_packet(skb))
7103df80d93SArnaldo Carvalho de Melo 		goto discard_it;
7113df80d93SArnaldo Carvalho de Melo 
7126f4e5fffSGerrit Renker 	/* Step 1: If header checksum is incorrect, drop packet and return. */
7130660e03fSArnaldo Carvalho de Melo 	if (dccp_v6_csum_finish(skb, &ipv6_hdr(skb)->saddr,
7140660e03fSArnaldo Carvalho de Melo 				     &ipv6_hdr(skb)->daddr)) {
71559348b19SGerrit Renker 		DCCP_WARN("dropped packet with invalid checksum\n");
7166f4e5fffSGerrit Renker 		goto discard_it;
7176f4e5fffSGerrit Renker 	}
7186f4e5fffSGerrit Renker 
7193df80d93SArnaldo Carvalho de Melo 	dh = dccp_hdr(skb);
7203df80d93SArnaldo Carvalho de Melo 
721fde20105SGerrit Renker 	DCCP_SKB_CB(skb)->dccpd_seq  = dccp_hdr_seq(dh);
7223df80d93SArnaldo Carvalho de Melo 	DCCP_SKB_CB(skb)->dccpd_type = dh->dccph_type;
7233df80d93SArnaldo Carvalho de Melo 
7243df80d93SArnaldo Carvalho de Melo 	if (dccp_packet_without_ack(skb))
7253df80d93SArnaldo Carvalho de Melo 		DCCP_SKB_CB(skb)->dccpd_ack_seq = DCCP_PKT_WITHOUT_ACK_SEQ;
7263df80d93SArnaldo Carvalho de Melo 	else
7273df80d93SArnaldo Carvalho de Melo 		DCCP_SKB_CB(skb)->dccpd_ack_seq = dccp_hdr_ack_seq(skb);
7283df80d93SArnaldo Carvalho de Melo 
7294bdc3d66SEric Dumazet lookup:
730a583636aSCraig Gallek 	sk = __inet6_lookup_skb(&dccp_hashinfo, skb, __dccp_hdr_len(dh),
731870c3151SEric Dumazet 			        dh->dccph_sport, dh->dccph_dport,
7324297a0efSDavid Ahern 				inet6_iif(skb), 0, &refcounted);
7334bdc3d66SEric Dumazet 	if (!sk) {
734d23c7107SGerrit Renker 		dccp_pr_debug("failed to look up flow ID in table and "
735d23c7107SGerrit Renker 			      "get corresponding socket\n");
7363df80d93SArnaldo Carvalho de Melo 		goto no_dccp_socket;
737d23c7107SGerrit Renker 	}
7383df80d93SArnaldo Carvalho de Melo 
7393df80d93SArnaldo Carvalho de Melo 	/*
7403df80d93SArnaldo Carvalho de Melo 	 * Step 2:
7413df80d93SArnaldo Carvalho de Melo 	 *	... or S.state == TIMEWAIT,
7423df80d93SArnaldo Carvalho de Melo 	 *		Generate Reset(No Connection) unless P.type == Reset
7433df80d93SArnaldo Carvalho de Melo 	 *		Drop packet and return
7443df80d93SArnaldo Carvalho de Melo 	 */
745d23c7107SGerrit Renker 	if (sk->sk_state == DCCP_TIME_WAIT) {
746d23c7107SGerrit Renker 		dccp_pr_debug("sk->sk_state == DCCP_TIME_WAIT: do_time_wait\n");
747d23c7107SGerrit Renker 		inet_twsk_put(inet_twsk(sk));
748d23c7107SGerrit Renker 		goto no_dccp_socket;
749d23c7107SGerrit Renker 	}
7503df80d93SArnaldo Carvalho de Melo 
751079096f1SEric Dumazet 	if (sk->sk_state == DCCP_NEW_SYN_RECV) {
752079096f1SEric Dumazet 		struct request_sock *req = inet_reqsk(sk);
7537716682cSEric Dumazet 		struct sock *nsk;
754079096f1SEric Dumazet 
755079096f1SEric Dumazet 		sk = req->rsk_listener;
7567716682cSEric Dumazet 		if (unlikely(sk->sk_state != DCCP_LISTEN)) {
757f03f2e15SEric Dumazet 			inet_csk_reqsk_queue_drop_and_put(sk, req);
7584bdc3d66SEric Dumazet 			goto lookup;
7594bdc3d66SEric Dumazet 		}
7607716682cSEric Dumazet 		sock_hold(sk);
7613b24d854SEric Dumazet 		refcounted = true;
7627716682cSEric Dumazet 		nsk = dccp_check_req(sk, skb, req);
763079096f1SEric Dumazet 		if (!nsk) {
764079096f1SEric Dumazet 			reqsk_put(req);
7657716682cSEric Dumazet 			goto discard_and_relse;
766079096f1SEric Dumazet 		}
767079096f1SEric Dumazet 		if (nsk == sk) {
768079096f1SEric Dumazet 			reqsk_put(req);
769079096f1SEric Dumazet 		} else if (dccp_child_process(sk, nsk, skb)) {
7706be49deaSJason Xing 			dccp_v6_ctl_send_reset(sk, skb, SK_RST_REASON_NOT_SPECIFIED);
7717716682cSEric Dumazet 			goto discard_and_relse;
772079096f1SEric Dumazet 		} else {
7737716682cSEric Dumazet 			sock_put(sk);
774079096f1SEric Dumazet 			return 0;
775079096f1SEric Dumazet 		}
776079096f1SEric Dumazet 	}
7776f4e5fffSGerrit Renker 	/*
7786f4e5fffSGerrit Renker 	 * RFC 4340, sec. 9.2.1: Minimum Checksum Coverage
7796f4e5fffSGerrit Renker 	 *	o if MinCsCov = 0, only packets with CsCov = 0 are accepted
7806f4e5fffSGerrit Renker 	 *	o if MinCsCov > 0, also accept packets with CsCov >= MinCsCov
7816f4e5fffSGerrit Renker 	 */
7826f4e5fffSGerrit Renker 	min_cov = dccp_sk(sk)->dccps_pcrlen;
7836f4e5fffSGerrit Renker 	if (dh->dccph_cscov  &&  (min_cov == 0 || dh->dccph_cscov < min_cov))  {
7846f4e5fffSGerrit Renker 		dccp_pr_debug("Packet CsCov %d does not satisfy MinCsCov %d\n",
7856f4e5fffSGerrit Renker 			      dh->dccph_cscov, min_cov);
7866f4e5fffSGerrit Renker 		/* FIXME: send Data Dropped option (see also dccp_v4_rcv) */
7876f4e5fffSGerrit Renker 		goto discard_and_relse;
7886f4e5fffSGerrit Renker 	}
7896f4e5fffSGerrit Renker 
7903df80d93SArnaldo Carvalho de Melo 	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
7913df80d93SArnaldo Carvalho de Melo 		goto discard_and_relse;
792b0e214d2SMadhu Koriginja 	nf_reset_ct(skb);
7933df80d93SArnaldo Carvalho de Melo 
794c3f24cfbSEric Dumazet 	return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4,
795c3f24cfbSEric Dumazet 				refcounted) ? -1 : 0;
7963df80d93SArnaldo Carvalho de Melo 
7973df80d93SArnaldo Carvalho de Melo no_dccp_socket:
7983df80d93SArnaldo Carvalho de Melo 	if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
7993df80d93SArnaldo Carvalho de Melo 		goto discard_it;
8003df80d93SArnaldo Carvalho de Melo 	/*
8013df80d93SArnaldo Carvalho de Melo 	 * Step 2:
802d83ca5acSGerrit Renker 	 *	If no socket ...
8033df80d93SArnaldo Carvalho de Melo 	 *		Generate Reset(No Connection) unless P.type == Reset
8043df80d93SArnaldo Carvalho de Melo 	 *		Drop packet and return
8053df80d93SArnaldo Carvalho de Melo 	 */
8063df80d93SArnaldo Carvalho de Melo 	if (dh->dccph_type != DCCP_PKT_RESET) {
8073df80d93SArnaldo Carvalho de Melo 		DCCP_SKB_CB(skb)->dccpd_reset_code =
8083df80d93SArnaldo Carvalho de Melo 					DCCP_RESET_CODE_NO_CONNECTION;
8096be49deaSJason Xing 		dccp_v6_ctl_send_reset(sk, skb, SK_RST_REASON_NOT_SPECIFIED);
8103df80d93SArnaldo Carvalho de Melo 	}
811d23c7107SGerrit Renker 
8123df80d93SArnaldo Carvalho de Melo discard_it:
8133df80d93SArnaldo Carvalho de Melo 	kfree_skb(skb);
8143df80d93SArnaldo Carvalho de Melo 	return 0;
8153df80d93SArnaldo Carvalho de Melo 
8163df80d93SArnaldo Carvalho de Melo discard_and_relse:
8173b24d854SEric Dumazet 	if (refcounted)
8183df80d93SArnaldo Carvalho de Melo 		sock_put(sk);
8193df80d93SArnaldo Carvalho de Melo 	goto discard_it;
8203df80d93SArnaldo Carvalho de Melo }
8213df80d93SArnaldo Carvalho de Melo 
82273c9e02cSGerrit Renker static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
82373c9e02cSGerrit Renker 			   int addr_len)
82473c9e02cSGerrit Renker {
82573c9e02cSGerrit Renker 	struct sockaddr_in6 *usin = (struct sockaddr_in6 *)uaddr;
82673c9e02cSGerrit Renker 	struct inet_connection_sock *icsk = inet_csk(sk);
82773c9e02cSGerrit Renker 	struct inet_sock *inet = inet_sk(sk);
82873c9e02cSGerrit Renker 	struct ipv6_pinfo *np = inet6_sk(sk);
82973c9e02cSGerrit Renker 	struct dccp_sock *dp = dccp_sk(sk);
83020c59de2SArnaud Ebalard 	struct in6_addr *saddr = NULL, *final_p, final;
83145f6fad8SEric Dumazet 	struct ipv6_txoptions *opt;
8324c9483b2SDavid S. Miller 	struct flowi6 fl6;
83373c9e02cSGerrit Renker 	struct dst_entry *dst;
83473c9e02cSGerrit Renker 	int addr_type;
83573c9e02cSGerrit Renker 	int err;
83673c9e02cSGerrit Renker 
83773c9e02cSGerrit Renker 	dp->dccps_role = DCCP_ROLE_CLIENT;
83873c9e02cSGerrit Renker 
83973c9e02cSGerrit Renker 	if (addr_len < SIN6_LEN_RFC2133)
84073c9e02cSGerrit Renker 		return -EINVAL;
84173c9e02cSGerrit Renker 
84273c9e02cSGerrit Renker 	if (usin->sin6_family != AF_INET6)
84373c9e02cSGerrit Renker 		return -EAFNOSUPPORT;
84473c9e02cSGerrit Renker 
8454c9483b2SDavid S. Miller 	memset(&fl6, 0, sizeof(fl6));
84673c9e02cSGerrit Renker 
847859f8b26SEric Dumazet 	if (inet6_test_bit(SNDFLOW, sk)) {
8484c9483b2SDavid S. Miller 		fl6.flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK;
8494c9483b2SDavid S. Miller 		IP6_ECN_flow_init(fl6.flowlabel);
8504c9483b2SDavid S. Miller 		if (fl6.flowlabel & IPV6_FLOWLABEL_MASK) {
85173c9e02cSGerrit Renker 			struct ip6_flowlabel *flowlabel;
8524c9483b2SDavid S. Miller 			flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
85359c820b2SWillem de Bruijn 			if (IS_ERR(flowlabel))
85473c9e02cSGerrit Renker 				return -EINVAL;
85573c9e02cSGerrit Renker 			fl6_sock_release(flowlabel);
85673c9e02cSGerrit Renker 		}
85773c9e02cSGerrit Renker 	}
85873c9e02cSGerrit Renker 	/*
85973c9e02cSGerrit Renker 	 * connect() to INADDR_ANY means loopback (BSD'ism).
86073c9e02cSGerrit Renker 	 */
86173c9e02cSGerrit Renker 	if (ipv6_addr_any(&usin->sin6_addr))
86273c9e02cSGerrit Renker 		usin->sin6_addr.s6_addr[15] = 1;
86373c9e02cSGerrit Renker 
86473c9e02cSGerrit Renker 	addr_type = ipv6_addr_type(&usin->sin6_addr);
86573c9e02cSGerrit Renker 
86673c9e02cSGerrit Renker 	if (addr_type & IPV6_ADDR_MULTICAST)
86773c9e02cSGerrit Renker 		return -ENETUNREACH;
86873c9e02cSGerrit Renker 
86973c9e02cSGerrit Renker 	if (addr_type & IPV6_ADDR_LINKLOCAL) {
87073c9e02cSGerrit Renker 		if (addr_len >= sizeof(struct sockaddr_in6) &&
87173c9e02cSGerrit Renker 		    usin->sin6_scope_id) {
87273c9e02cSGerrit Renker 			/* If interface is set while binding, indices
87373c9e02cSGerrit Renker 			 * must coincide.
87473c9e02cSGerrit Renker 			 */
87573c9e02cSGerrit Renker 			if (sk->sk_bound_dev_if &&
87673c9e02cSGerrit Renker 			    sk->sk_bound_dev_if != usin->sin6_scope_id)
87773c9e02cSGerrit Renker 				return -EINVAL;
87873c9e02cSGerrit Renker 
87973c9e02cSGerrit Renker 			sk->sk_bound_dev_if = usin->sin6_scope_id;
88073c9e02cSGerrit Renker 		}
88173c9e02cSGerrit Renker 
88273c9e02cSGerrit Renker 		/* Connect to link-local address requires an interface */
88373c9e02cSGerrit Renker 		if (!sk->sk_bound_dev_if)
88473c9e02cSGerrit Renker 			return -EINVAL;
88573c9e02cSGerrit Renker 	}
88673c9e02cSGerrit Renker 
887efe4208fSEric Dumazet 	sk->sk_v6_daddr = usin->sin6_addr;
8884c9483b2SDavid S. Miller 	np->flow_label = fl6.flowlabel;
88973c9e02cSGerrit Renker 
89073c9e02cSGerrit Renker 	/*
89173c9e02cSGerrit Renker 	 * DCCP over IPv4
89273c9e02cSGerrit Renker 	 */
89373c9e02cSGerrit Renker 	if (addr_type == IPV6_ADDR_MAPPED) {
89473c9e02cSGerrit Renker 		u32 exthdrlen = icsk->icsk_ext_hdr_len;
89573c9e02cSGerrit Renker 		struct sockaddr_in sin;
89673c9e02cSGerrit Renker 
8978e5443d2SDenis Kirjanov 		net_dbg_ratelimited("connect: ipv4 mapped\n");
89873c9e02cSGerrit Renker 
89989e9c728SKuniyuki Iwashima 		if (ipv6_only_sock(sk))
90073c9e02cSGerrit Renker 			return -ENETUNREACH;
90173c9e02cSGerrit Renker 
90273c9e02cSGerrit Renker 		sin.sin_family = AF_INET;
90373c9e02cSGerrit Renker 		sin.sin_port = usin->sin6_port;
90473c9e02cSGerrit Renker 		sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
90573c9e02cSGerrit Renker 
90673c9e02cSGerrit Renker 		icsk->icsk_af_ops = &dccp_ipv6_mapped;
90773c9e02cSGerrit Renker 		sk->sk_backlog_rcv = dccp_v4_do_rcv;
90873c9e02cSGerrit Renker 
90973c9e02cSGerrit Renker 		err = dccp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
91073c9e02cSGerrit Renker 		if (err) {
91173c9e02cSGerrit Renker 			icsk->icsk_ext_hdr_len = exthdrlen;
91273c9e02cSGerrit Renker 			icsk->icsk_af_ops = &dccp_ipv6_af_ops;
91373c9e02cSGerrit Renker 			sk->sk_backlog_rcv = dccp_v6_do_rcv;
91473c9e02cSGerrit Renker 			goto failure;
91573c9e02cSGerrit Renker 		}
916d1e559d0SEric Dumazet 		np->saddr = sk->sk_v6_rcv_saddr;
91773c9e02cSGerrit Renker 		return err;
91873c9e02cSGerrit Renker 	}
91973c9e02cSGerrit Renker 
920efe4208fSEric Dumazet 	if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr))
921efe4208fSEric Dumazet 		saddr = &sk->sk_v6_rcv_saddr;
92273c9e02cSGerrit Renker 
9234c9483b2SDavid S. Miller 	fl6.flowi6_proto = IPPROTO_DCCP;
924efe4208fSEric Dumazet 	fl6.daddr = sk->sk_v6_daddr;
9254e3fd7a0SAlexey Dobriyan 	fl6.saddr = saddr ? *saddr : np->saddr;
9264c9483b2SDavid S. Miller 	fl6.flowi6_oif = sk->sk_bound_dev_if;
9271958b856SDavid S. Miller 	fl6.fl6_dport = usin->sin6_port;
9281958b856SDavid S. Miller 	fl6.fl6_sport = inet->inet_sport;
9293df98d79SPaul Moore 	security_sk_classify_flow(sk, flowi6_to_flowi_common(&fl6));
93073c9e02cSGerrit Renker 
9311e1d04e6SHannes Frederic Sowa 	opt = rcu_dereference_protected(np->opt, lockdep_sock_is_held(sk));
93245f6fad8SEric Dumazet 	final_p = fl6_update_dst(&fl6, opt, &final);
93373c9e02cSGerrit Renker 
934c4e85f73SSabrina Dubroca 	dst = ip6_dst_lookup_flow(sock_net(sk), sk, &fl6, final_p);
93568d0c6d3SDavid S. Miller 	if (IS_ERR(dst)) {
93668d0c6d3SDavid S. Miller 		err = PTR_ERR(dst);
93773c9e02cSGerrit Renker 		goto failure;
93814e50e57SDavid S. Miller 	}
93973c9e02cSGerrit Renker 
94073c9e02cSGerrit Renker 	if (saddr == NULL) {
9414c9483b2SDavid S. Miller 		saddr = &fl6.saddr;
94228044fc1SJoanne Koong 
9438c5dae4cSKuniyuki Iwashima 		err = inet_bhash2_update_saddr(sk, saddr, AF_INET6);
9448c5dae4cSKuniyuki Iwashima 		if (err)
94528044fc1SJoanne Koong 			goto failure;
94628044fc1SJoanne Koong 	}
94773c9e02cSGerrit Renker 
94873c9e02cSGerrit Renker 	/* set the source address */
9494e3fd7a0SAlexey Dobriyan 	np->saddr = *saddr;
950c720c7e8SEric Dumazet 	inet->inet_rcv_saddr = LOOPBACK4_IPV6;
95173c9e02cSGerrit Renker 
9526bd4f355SEric Dumazet 	ip6_dst_store(sk, dst, NULL, NULL);
95373c9e02cSGerrit Renker 
95473c9e02cSGerrit Renker 	icsk->icsk_ext_hdr_len = 0;
95545f6fad8SEric Dumazet 	if (opt)
95645f6fad8SEric Dumazet 		icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen;
95773c9e02cSGerrit Renker 
958c720c7e8SEric Dumazet 	inet->inet_dport = usin->sin6_port;
95973c9e02cSGerrit Renker 
96073c9e02cSGerrit Renker 	dccp_set_state(sk, DCCP_REQUESTING);
96173c9e02cSGerrit Renker 	err = inet6_hash_connect(&dccp_death_row, sk);
96273c9e02cSGerrit Renker 	if (err)
96373c9e02cSGerrit Renker 		goto late_failure;
964d7f7365fSGerrit Renker 
965d7f7365fSGerrit Renker 	dp->dccps_iss = secure_dccpv6_sequence_number(np->saddr.s6_addr32,
966efe4208fSEric Dumazet 						      sk->sk_v6_daddr.s6_addr32,
967c720c7e8SEric Dumazet 						      inet->inet_sport,
968c720c7e8SEric Dumazet 						      inet->inet_dport);
96973c9e02cSGerrit Renker 	err = dccp_connect(sk);
97073c9e02cSGerrit Renker 	if (err)
97173c9e02cSGerrit Renker 		goto late_failure;
97273c9e02cSGerrit Renker 
97373c9e02cSGerrit Renker 	return 0;
97473c9e02cSGerrit Renker 
97573c9e02cSGerrit Renker late_failure:
97673c9e02cSGerrit Renker 	dccp_set_state(sk, DCCP_CLOSED);
977e0833d1fSKuniyuki Iwashima 	inet_bhash2_reset_saddr(sk);
97873c9e02cSGerrit Renker 	__sk_dst_reset(sk);
97973c9e02cSGerrit Renker failure:
980c720c7e8SEric Dumazet 	inet->inet_dport = 0;
98173c9e02cSGerrit Renker 	sk->sk_route_caps = 0;
98273c9e02cSGerrit Renker 	return err;
98373c9e02cSGerrit Renker }
98473c9e02cSGerrit Renker 
9853b401a81SStephen Hemminger static const struct inet_connection_sock_af_ops dccp_ipv6_af_ops = {
9863df80d93SArnaldo Carvalho de Melo 	.queue_xmit	   = inet6_csk_xmit,
9873df80d93SArnaldo Carvalho de Melo 	.send_check	   = dccp_v6_send_check,
9883df80d93SArnaldo Carvalho de Melo 	.rebuild_header	   = inet6_sk_rebuild_header,
9893df80d93SArnaldo Carvalho de Melo 	.conn_request	   = dccp_v6_conn_request,
9903df80d93SArnaldo Carvalho de Melo 	.syn_recv_sock	   = dccp_v6_request_recv_sock,
9913df80d93SArnaldo Carvalho de Melo 	.net_header_len	   = sizeof(struct ipv6hdr),
9923df80d93SArnaldo Carvalho de Melo 	.setsockopt	   = ipv6_setsockopt,
9933df80d93SArnaldo Carvalho de Melo 	.getsockopt	   = ipv6_getsockopt,
994543d9cfeSArnaldo Carvalho de Melo 	.addr2sockaddr	   = inet6_csk_addr2sockaddr,
995543d9cfeSArnaldo Carvalho de Melo 	.sockaddr_len	   = sizeof(struct sockaddr_in6),
9963df80d93SArnaldo Carvalho de Melo };
9973df80d93SArnaldo Carvalho de Melo 
9983df80d93SArnaldo Carvalho de Melo /*
9993df80d93SArnaldo Carvalho de Melo  *	DCCP over IPv4 via INET6 API
10003df80d93SArnaldo Carvalho de Melo  */
10013b401a81SStephen Hemminger static const struct inet_connection_sock_af_ops dccp_ipv6_mapped = {
10023df80d93SArnaldo Carvalho de Melo 	.queue_xmit	   = ip_queue_xmit,
10033df80d93SArnaldo Carvalho de Melo 	.send_check	   = dccp_v4_send_check,
10043df80d93SArnaldo Carvalho de Melo 	.rebuild_header	   = inet_sk_rebuild_header,
10053df80d93SArnaldo Carvalho de Melo 	.conn_request	   = dccp_v6_conn_request,
10063df80d93SArnaldo Carvalho de Melo 	.syn_recv_sock	   = dccp_v6_request_recv_sock,
10073df80d93SArnaldo Carvalho de Melo 	.net_header_len	   = sizeof(struct iphdr),
10083df80d93SArnaldo Carvalho de Melo 	.setsockopt	   = ipv6_setsockopt,
10093df80d93SArnaldo Carvalho de Melo 	.getsockopt	   = ipv6_getsockopt,
1010543d9cfeSArnaldo Carvalho de Melo 	.addr2sockaddr	   = inet6_csk_addr2sockaddr,
1011543d9cfeSArnaldo Carvalho de Melo 	.sockaddr_len	   = sizeof(struct sockaddr_in6),
10123df80d93SArnaldo Carvalho de Melo };
10133df80d93SArnaldo Carvalho de Melo 
10141651951eSKuniyuki Iwashima static void dccp_v6_sk_destruct(struct sock *sk)
10151651951eSKuniyuki Iwashima {
10161651951eSKuniyuki Iwashima 	dccp_destruct_common(sk);
10171651951eSKuniyuki Iwashima 	inet6_sock_destruct(sk);
10181651951eSKuniyuki Iwashima }
10191651951eSKuniyuki Iwashima 
10203df80d93SArnaldo Carvalho de Melo /* NOTE: A lot of things set to zero explicitly by call to
10213df80d93SArnaldo Carvalho de Melo  *       sk_alloc() so need not be done here.
10223df80d93SArnaldo Carvalho de Melo  */
10233df80d93SArnaldo Carvalho de Melo static int dccp_v6_init_sock(struct sock *sk)
10243df80d93SArnaldo Carvalho de Melo {
102572478873SArnaldo Carvalho de Melo 	static __u8 dccp_v6_ctl_sock_initialized;
102672478873SArnaldo Carvalho de Melo 	int err = dccp_init_sock(sk, dccp_v6_ctl_sock_initialized);
10273df80d93SArnaldo Carvalho de Melo 
102872478873SArnaldo Carvalho de Melo 	if (err == 0) {
102972478873SArnaldo Carvalho de Melo 		if (unlikely(!dccp_v6_ctl_sock_initialized))
103072478873SArnaldo Carvalho de Melo 			dccp_v6_ctl_sock_initialized = 1;
10313df80d93SArnaldo Carvalho de Melo 		inet_csk(sk)->icsk_af_ops = &dccp_ipv6_af_ops;
10321651951eSKuniyuki Iwashima 		sk->sk_destruct = dccp_v6_sk_destruct;
103372478873SArnaldo Carvalho de Melo 	}
10343df80d93SArnaldo Carvalho de Melo 
10353df80d93SArnaldo Carvalho de Melo 	return err;
10363df80d93SArnaldo Carvalho de Melo }
10373df80d93SArnaldo Carvalho de Melo 
103873c9e02cSGerrit Renker static struct timewait_sock_ops dccp6_timewait_sock_ops = {
103973c9e02cSGerrit Renker 	.twsk_obj_size	= sizeof(struct dccp6_timewait_sock),
104073c9e02cSGerrit Renker };
104173c9e02cSGerrit Renker 
10423df80d93SArnaldo Carvalho de Melo static struct proto dccp_v6_prot = {
10433df80d93SArnaldo Carvalho de Melo 	.name		   = "DCCPv6",
10443df80d93SArnaldo Carvalho de Melo 	.owner		   = THIS_MODULE,
10453df80d93SArnaldo Carvalho de Melo 	.close		   = dccp_close,
10463df80d93SArnaldo Carvalho de Melo 	.connect	   = dccp_v6_connect,
10473df80d93SArnaldo Carvalho de Melo 	.disconnect	   = dccp_disconnect,
10483df80d93SArnaldo Carvalho de Melo 	.ioctl		   = dccp_ioctl,
10493df80d93SArnaldo Carvalho de Melo 	.init		   = dccp_v6_init_sock,
10503df80d93SArnaldo Carvalho de Melo 	.setsockopt	   = dccp_setsockopt,
10513df80d93SArnaldo Carvalho de Melo 	.getsockopt	   = dccp_getsockopt,
10523df80d93SArnaldo Carvalho de Melo 	.sendmsg	   = dccp_sendmsg,
10533df80d93SArnaldo Carvalho de Melo 	.recvmsg	   = dccp_recvmsg,
10543df80d93SArnaldo Carvalho de Melo 	.backlog_rcv	   = dccp_v6_do_rcv,
1055496611d7SCraig Gallek 	.hash		   = inet6_hash,
1056ab1e0a13SArnaldo Carvalho de Melo 	.unhash		   = inet_unhash,
10573df80d93SArnaldo Carvalho de Melo 	.accept		   = inet_csk_accept,
1058ab1e0a13SArnaldo Carvalho de Melo 	.get_port	   = inet_csk_get_port,
10593df80d93SArnaldo Carvalho de Melo 	.shutdown	   = dccp_shutdown,
10601651951eSKuniyuki Iwashima 	.destroy	   = dccp_destroy_sock,
10613df80d93SArnaldo Carvalho de Melo 	.orphan_count	   = &dccp_orphan_count,
10623df80d93SArnaldo Carvalho de Melo 	.max_header	   = MAX_DCCP_HEADER,
10633df80d93SArnaldo Carvalho de Melo 	.obj_size	   = sizeof(struct dccp6_sock),
1064f5f80e32SEric Dumazet 	.ipv6_pinfo_offset = offsetof(struct dccp6_sock, inet6),
10655f0d5a3aSPaul E. McKenney 	.slab_flags	   = SLAB_TYPESAFE_BY_RCU,
10663df80d93SArnaldo Carvalho de Melo 	.rsk_prot	   = &dccp6_request_sock_ops,
10676d6ee43eSArnaldo Carvalho de Melo 	.twsk_prot	   = &dccp6_timewait_sock_ops,
106839d8cda7SPavel Emelyanov 	.h.hashinfo	   = &dccp_hashinfo,
10693df80d93SArnaldo Carvalho de Melo };
10703df80d93SArnaldo Carvalho de Melo 
107141135cc8SAlexey Dobriyan static const struct inet6_protocol dccp_v6_protocol = {
10723df80d93SArnaldo Carvalho de Melo 	.handler	= dccp_v6_rcv,
10733df80d93SArnaldo Carvalho de Melo 	.err_handler	= dccp_v6_err,
10743df80d93SArnaldo Carvalho de Melo 	.flags		= INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL,
10753df80d93SArnaldo Carvalho de Melo };
10763df80d93SArnaldo Carvalho de Melo 
10775708e868SAlexey Dobriyan static const struct proto_ops inet6_dccp_ops = {
10783df80d93SArnaldo Carvalho de Melo 	.family		   = PF_INET6,
10793df80d93SArnaldo Carvalho de Melo 	.owner		   = THIS_MODULE,
10803df80d93SArnaldo Carvalho de Melo 	.release	   = inet6_release,
10813df80d93SArnaldo Carvalho de Melo 	.bind		   = inet6_bind,
10823df80d93SArnaldo Carvalho de Melo 	.connect	   = inet_stream_connect,
10833df80d93SArnaldo Carvalho de Melo 	.socketpair	   = sock_no_socketpair,
10843df80d93SArnaldo Carvalho de Melo 	.accept		   = inet_accept,
10853df80d93SArnaldo Carvalho de Melo 	.getname	   = inet6_getname,
1086a11e1d43SLinus Torvalds 	.poll		   = dccp_poll,
10873df80d93SArnaldo Carvalho de Melo 	.ioctl		   = inet6_ioctl,
1088c7cbdbf2SArnd Bergmann 	.gettstamp	   = sock_gettstamp,
10893df80d93SArnaldo Carvalho de Melo 	.listen		   = inet_dccp_listen,
10903df80d93SArnaldo Carvalho de Melo 	.shutdown	   = inet_shutdown,
10913df80d93SArnaldo Carvalho de Melo 	.setsockopt	   = sock_common_setsockopt,
10923df80d93SArnaldo Carvalho de Melo 	.getsockopt	   = sock_common_getsockopt,
10933df80d93SArnaldo Carvalho de Melo 	.sendmsg	   = inet_sendmsg,
10943df80d93SArnaldo Carvalho de Melo 	.recvmsg	   = sock_common_recvmsg,
10953df80d93SArnaldo Carvalho de Melo 	.mmap		   = sock_no_mmap,
1096543d9cfeSArnaldo Carvalho de Melo #ifdef CONFIG_COMPAT
10973986912fSChristoph Hellwig 	.compat_ioctl	   = inet6_compat_ioctl,
1098543d9cfeSArnaldo Carvalho de Melo #endif
10993df80d93SArnaldo Carvalho de Melo };
11003df80d93SArnaldo Carvalho de Melo 
11013df80d93SArnaldo Carvalho de Melo static struct inet_protosw dccp_v6_protosw = {
11023df80d93SArnaldo Carvalho de Melo 	.type		= SOCK_DCCP,
11033df80d93SArnaldo Carvalho de Melo 	.protocol	= IPPROTO_DCCP,
11043df80d93SArnaldo Carvalho de Melo 	.prot		= &dccp_v6_prot,
11053df80d93SArnaldo Carvalho de Melo 	.ops		= &inet6_dccp_ops,
1106d83d8461SArnaldo Carvalho de Melo 	.flags		= INET_PROTOSW_ICSK,
11073df80d93SArnaldo Carvalho de Melo };
11083df80d93SArnaldo Carvalho de Melo 
11092c8c1e72SAlexey Dobriyan static int __net_init dccp_v6_init_net(struct net *net)
11108231bd27SPavel Emelyanov {
1111b98b3304SFlorian Westphal 	struct dccp_v6_pernet *pn = net_generic(net, dccp_v6_pernet_id);
1112b98b3304SFlorian Westphal 
1113d14a0ebdSGerrit Renker 	if (dccp_hashinfo.bhash == NULL)
1114d14a0ebdSGerrit Renker 		return -ESOCKTNOSUPPORT;
1115334527d3SPavel Emelyanov 
1116b98b3304SFlorian Westphal 	return inet_ctl_sock_create(&pn->v6_ctl_sk, PF_INET6,
1117334527d3SPavel Emelyanov 				    SOCK_DCCP, IPPROTO_DCCP, net);
11188231bd27SPavel Emelyanov }
11198231bd27SPavel Emelyanov 
11202c8c1e72SAlexey Dobriyan static void __net_exit dccp_v6_exit_net(struct net *net)
11218231bd27SPavel Emelyanov {
1122b98b3304SFlorian Westphal 	struct dccp_v6_pernet *pn = net_generic(net, dccp_v6_pernet_id);
1123b98b3304SFlorian Westphal 
1124b98b3304SFlorian Westphal 	inet_ctl_sock_destroy(pn->v6_ctl_sk);
11258231bd27SPavel Emelyanov }
11268231bd27SPavel Emelyanov 
11278231bd27SPavel Emelyanov static struct pernet_operations dccp_v6_ops = {
11288231bd27SPavel Emelyanov 	.init   = dccp_v6_init_net,
11298231bd27SPavel Emelyanov 	.exit   = dccp_v6_exit_net,
1130b98b3304SFlorian Westphal 	.id	= &dccp_v6_pernet_id,
1131b98b3304SFlorian Westphal 	.size   = sizeof(struct dccp_v6_pernet),
11328231bd27SPavel Emelyanov };
11338231bd27SPavel Emelyanov 
11343df80d93SArnaldo Carvalho de Melo static int __init dccp_v6_init(void)
11353df80d93SArnaldo Carvalho de Melo {
11363df80d93SArnaldo Carvalho de Melo 	int err = proto_register(&dccp_v6_prot, 1);
11373df80d93SArnaldo Carvalho de Melo 
1138a0f9a4c2SXin Long 	if (err)
11393df80d93SArnaldo Carvalho de Melo 		goto out;
11403df80d93SArnaldo Carvalho de Melo 
11413df80d93SArnaldo Carvalho de Melo 	inet6_register_protosw(&dccp_v6_protosw);
114272478873SArnaldo Carvalho de Melo 
11438231bd27SPavel Emelyanov 	err = register_pernet_subsys(&dccp_v6_ops);
1144a0f9a4c2SXin Long 	if (err)
11458231bd27SPavel Emelyanov 		goto out_destroy_ctl_sock;
1146a0f9a4c2SXin Long 
1147a0f9a4c2SXin Long 	err = inet6_add_protocol(&dccp_v6_protocol, IPPROTO_DCCP);
1148a0f9a4c2SXin Long 	if (err)
1149a0f9a4c2SXin Long 		goto out_unregister_proto;
1150a0f9a4c2SXin Long 
11513df80d93SArnaldo Carvalho de Melo out:
11523df80d93SArnaldo Carvalho de Melo 	return err;
11533df80d93SArnaldo Carvalho de Melo out_unregister_proto:
1154a0f9a4c2SXin Long 	unregister_pernet_subsys(&dccp_v6_ops);
1155a0f9a4c2SXin Long out_destroy_ctl_sock:
1156a0f9a4c2SXin Long 	inet6_unregister_protosw(&dccp_v6_protosw);
11573df80d93SArnaldo Carvalho de Melo 	proto_unregister(&dccp_v6_prot);
11583df80d93SArnaldo Carvalho de Melo 	goto out;
11593df80d93SArnaldo Carvalho de Melo }
11603df80d93SArnaldo Carvalho de Melo 
11613df80d93SArnaldo Carvalho de Melo static void __exit dccp_v6_exit(void)
11623df80d93SArnaldo Carvalho de Melo {
11633df80d93SArnaldo Carvalho de Melo 	inet6_del_protocol(&dccp_v6_protocol, IPPROTO_DCCP);
1164a0f9a4c2SXin Long 	unregister_pernet_subsys(&dccp_v6_ops);
11653df80d93SArnaldo Carvalho de Melo 	inet6_unregister_protosw(&dccp_v6_protosw);
11663df80d93SArnaldo Carvalho de Melo 	proto_unregister(&dccp_v6_prot);
11673df80d93SArnaldo Carvalho de Melo }
11683df80d93SArnaldo Carvalho de Melo 
11693df80d93SArnaldo Carvalho de Melo module_init(dccp_v6_init);
11703df80d93SArnaldo Carvalho de Melo module_exit(dccp_v6_exit);
11713df80d93SArnaldo Carvalho de Melo 
11723df80d93SArnaldo Carvalho de Melo /*
11733df80d93SArnaldo Carvalho de Melo  * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33)
11743df80d93SArnaldo Carvalho de Melo  * values directly, Also cover the case where the protocol is not specified,
11753df80d93SArnaldo Carvalho de Melo  * i.e. net-pf-PF_INET6-proto-0-type-SOCK_DCCP
11763df80d93SArnaldo Carvalho de Melo  */
11777131c6c7SJean Delvare MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 33, 6);
11787131c6c7SJean Delvare MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 0, 6);
11793df80d93SArnaldo Carvalho de Melo MODULE_LICENSE("GPL");
11803df80d93SArnaldo Carvalho de Melo MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>");
11813df80d93SArnaldo Carvalho de Melo MODULE_DESCRIPTION("DCCPv6 - Datagram Congestion Controlled Protocol");
1182