xref: /openbmc/linux/net/dccp/ipv6.c (revision 093d282321daeb19c107e5f1f16d7f68484f3ade)
13df80d93SArnaldo Carvalho de Melo /*
23df80d93SArnaldo Carvalho de Melo  *	DCCP over IPv6
33df80d93SArnaldo Carvalho de Melo  *	Linux INET6 implementation
43df80d93SArnaldo Carvalho de Melo  *
53df80d93SArnaldo Carvalho de Melo  *	Based on net/dccp6/ipv6.c
63df80d93SArnaldo Carvalho de Melo  *
73df80d93SArnaldo Carvalho de Melo  *	Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
83df80d93SArnaldo Carvalho de Melo  *
93df80d93SArnaldo Carvalho de Melo  *	This program is free software; you can redistribute it and/or
103df80d93SArnaldo Carvalho de Melo  *      modify it under the terms of the GNU General Public License
113df80d93SArnaldo Carvalho de Melo  *      as published by the Free Software Foundation; either version
123df80d93SArnaldo Carvalho de Melo  *      2 of the License, or (at your option) any later version.
133df80d93SArnaldo Carvalho de Melo  */
143df80d93SArnaldo Carvalho de Melo 
153df80d93SArnaldo Carvalho de Melo #include <linux/module.h>
163df80d93SArnaldo Carvalho de Melo #include <linux/random.h>
175a0e3ad6STejun Heo #include <linux/slab.h>
183df80d93SArnaldo Carvalho de Melo #include <linux/xfrm.h>
193df80d93SArnaldo Carvalho de Melo 
203df80d93SArnaldo Carvalho de Melo #include <net/addrconf.h>
213df80d93SArnaldo Carvalho de Melo #include <net/inet_common.h>
223df80d93SArnaldo Carvalho de Melo #include <net/inet_hashtables.h>
2314c85021SArnaldo Carvalho de Melo #include <net/inet_sock.h>
243df80d93SArnaldo Carvalho de Melo #include <net/inet6_connection_sock.h>
253df80d93SArnaldo Carvalho de Melo #include <net/inet6_hashtables.h>
263df80d93SArnaldo Carvalho de Melo #include <net/ip6_route.h>
273df80d93SArnaldo Carvalho de Melo #include <net/ipv6.h>
283df80d93SArnaldo Carvalho de Melo #include <net/protocol.h>
293df80d93SArnaldo Carvalho de Melo #include <net/transp_v6.h>
30aa0e4e4aSDavid S. Miller #include <net/ip6_checksum.h>
313df80d93SArnaldo Carvalho de Melo #include <net/xfrm.h>
323df80d93SArnaldo Carvalho de Melo 
333df80d93SArnaldo Carvalho de Melo #include "dccp.h"
343df80d93SArnaldo Carvalho de Melo #include "ipv6.h"
354b79f0afSIan McDonald #include "feat.h"
363df80d93SArnaldo Carvalho de Melo 
3713f51d82SPavel Emelyanov /* The per-net dccp.v6_ctl_sk is used for sending RSTs and ACKs */
3872478873SArnaldo Carvalho de Melo 
393b401a81SStephen Hemminger static const struct inet_connection_sock_af_ops dccp_ipv6_mapped;
403b401a81SStephen Hemminger static const struct inet_connection_sock_af_ops dccp_ipv6_af_ops;
413df80d93SArnaldo Carvalho de Melo 
423df80d93SArnaldo Carvalho de Melo static void dccp_v6_hash(struct sock *sk)
433df80d93SArnaldo Carvalho de Melo {
443df80d93SArnaldo Carvalho de Melo 	if (sk->sk_state != DCCP_CLOSED) {
453df80d93SArnaldo Carvalho de Melo 		if (inet_csk(sk)->icsk_af_ops == &dccp_ipv6_mapped) {
46ab1e0a13SArnaldo Carvalho de Melo 			inet_hash(sk);
473df80d93SArnaldo Carvalho de Melo 			return;
483df80d93SArnaldo Carvalho de Melo 		}
493df80d93SArnaldo Carvalho de Melo 		local_bh_disable();
509327f705SEric Dumazet 		__inet6_hash(sk, NULL);
513df80d93SArnaldo Carvalho de Melo 		local_bh_enable();
523df80d93SArnaldo Carvalho de Melo 	}
533df80d93SArnaldo Carvalho de Melo }
543df80d93SArnaldo Carvalho de Melo 
556f4e5fffSGerrit Renker /* add pseudo-header to DCCP checksum stored in skb->csum */
56868c86bcSAl Viro static inline __sum16 dccp_v6_csum_finish(struct sk_buff *skb,
573df80d93SArnaldo Carvalho de Melo 				      struct in6_addr *saddr,
586f4e5fffSGerrit Renker 				      struct in6_addr *daddr)
593df80d93SArnaldo Carvalho de Melo {
606f4e5fffSGerrit Renker 	return csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_DCCP, skb->csum);
616f4e5fffSGerrit Renker }
626f4e5fffSGerrit Renker 
63bb296246SHerbert Xu static inline void dccp_v6_send_check(struct sock *sk, struct sk_buff *skb)
646f4e5fffSGerrit Renker {
656f4e5fffSGerrit Renker 	struct ipv6_pinfo *np = inet6_sk(sk);
666f4e5fffSGerrit Renker 	struct dccp_hdr *dh = dccp_hdr(skb);
676f4e5fffSGerrit Renker 
686f4e5fffSGerrit Renker 	dccp_csum_outgoing(skb);
696f4e5fffSGerrit Renker 	dh->dccph_checksum = dccp_v6_csum_finish(skb, &np->saddr, &np->daddr);
703df80d93SArnaldo Carvalho de Melo }
713df80d93SArnaldo Carvalho de Melo 
727d533f94SAl Viro static inline __u32 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr,
737d533f94SAl Viro 						  __be16 sport, __be16 dport   )
743df80d93SArnaldo Carvalho de Melo {
75d7f7365fSGerrit Renker 	return secure_tcpv6_sequence_number(saddr, daddr, sport, dport);
76d7f7365fSGerrit Renker }
77d7f7365fSGerrit Renker 
78d7f7365fSGerrit Renker static inline __u32 dccp_v6_init_sequence(struct sk_buff *skb)
79d7f7365fSGerrit Renker {
800660e03fSArnaldo Carvalho de Melo 	return secure_dccpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32,
810660e03fSArnaldo Carvalho de Melo 					     ipv6_hdr(skb)->saddr.s6_addr32,
82865e9022SGerrit Renker 					     dccp_hdr(skb)->dccph_dport,
83865e9022SGerrit Renker 					     dccp_hdr(skb)->dccph_sport     );
84d7f7365fSGerrit Renker 
853df80d93SArnaldo Carvalho de Melo }
863df80d93SArnaldo Carvalho de Melo 
873df80d93SArnaldo Carvalho de Melo static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
88d5fdd6baSBrian Haley 			u8 type, u8 code, int offset, __be32 info)
893df80d93SArnaldo Carvalho de Melo {
903df80d93SArnaldo Carvalho de Melo 	struct ipv6hdr *hdr = (struct ipv6hdr *)skb->data;
913df80d93SArnaldo Carvalho de Melo 	const struct dccp_hdr *dh = (struct dccp_hdr *)(skb->data + offset);
92e0bcfb0cSWei Yongjun 	struct dccp_sock *dp;
933df80d93SArnaldo Carvalho de Melo 	struct ipv6_pinfo *np;
943df80d93SArnaldo Carvalho de Melo 	struct sock *sk;
953df80d93SArnaldo Carvalho de Melo 	int err;
963df80d93SArnaldo Carvalho de Melo 	__u64 seq;
97ca12a1a4SPavel Emelyanov 	struct net *net = dev_net(skb->dev);
983df80d93SArnaldo Carvalho de Melo 
99860239c5SWei Yongjun 	if (skb->len < offset + sizeof(*dh) ||
100860239c5SWei Yongjun 	    skb->len < offset + __dccp_basic_hdr_len(dh)) {
101e41b5368SDenis V. Lunev 		ICMP6_INC_STATS_BH(net, __in6_dev_get(skb->dev),
102e41b5368SDenis V. Lunev 				   ICMP6_MIB_INERRORS);
103860239c5SWei Yongjun 		return;
104860239c5SWei Yongjun 	}
105860239c5SWei Yongjun 
106ca12a1a4SPavel Emelyanov 	sk = inet6_lookup(net, &dccp_hashinfo,
107671a1c74SPavel Emelyanov 			&hdr->daddr, dh->dccph_dport,
108f2776ff0SYOSHIFUJI Hideaki 			&hdr->saddr, dh->dccph_sport, inet6_iif(skb));
1093df80d93SArnaldo Carvalho de Melo 
1103df80d93SArnaldo Carvalho de Melo 	if (sk == NULL) {
111e41b5368SDenis V. Lunev 		ICMP6_INC_STATS_BH(net, __in6_dev_get(skb->dev),
112e41b5368SDenis V. Lunev 				   ICMP6_MIB_INERRORS);
1133df80d93SArnaldo Carvalho de Melo 		return;
1143df80d93SArnaldo Carvalho de Melo 	}
1153df80d93SArnaldo Carvalho de Melo 
1163df80d93SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_TIME_WAIT) {
1179469c7b4SYOSHIFUJI Hideaki 		inet_twsk_put(inet_twsk(sk));
1183df80d93SArnaldo Carvalho de Melo 		return;
1193df80d93SArnaldo Carvalho de Melo 	}
1203df80d93SArnaldo Carvalho de Melo 
1213df80d93SArnaldo Carvalho de Melo 	bh_lock_sock(sk);
1223df80d93SArnaldo Carvalho de Melo 	if (sock_owned_by_user(sk))
123de0744afSPavel Emelyanov 		NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS);
1243df80d93SArnaldo Carvalho de Melo 
1253df80d93SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_CLOSED)
1263df80d93SArnaldo Carvalho de Melo 		goto out;
1273df80d93SArnaldo Carvalho de Melo 
128e0bcfb0cSWei Yongjun 	dp = dccp_sk(sk);
129e0bcfb0cSWei Yongjun 	seq = dccp_hdr_seq(dh);
130e0bcfb0cSWei Yongjun 	if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_LISTEN) &&
131e0bcfb0cSWei Yongjun 	    !between48(seq, dp->dccps_awl, dp->dccps_awh)) {
132e0bcfb0cSWei Yongjun 		NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
133e0bcfb0cSWei Yongjun 		goto out;
134e0bcfb0cSWei Yongjun 	}
135e0bcfb0cSWei Yongjun 
1363df80d93SArnaldo Carvalho de Melo 	np = inet6_sk(sk);
1373df80d93SArnaldo Carvalho de Melo 
1383df80d93SArnaldo Carvalho de Melo 	if (type == ICMPV6_PKT_TOOBIG) {
1393df80d93SArnaldo Carvalho de Melo 		struct dst_entry *dst = NULL;
1403df80d93SArnaldo Carvalho de Melo 
1413df80d93SArnaldo Carvalho de Melo 		if (sock_owned_by_user(sk))
1423df80d93SArnaldo Carvalho de Melo 			goto out;
1433df80d93SArnaldo Carvalho de Melo 		if ((1 << sk->sk_state) & (DCCPF_LISTEN | DCCPF_CLOSED))
1443df80d93SArnaldo Carvalho de Melo 			goto out;
1453df80d93SArnaldo Carvalho de Melo 
1463df80d93SArnaldo Carvalho de Melo 		/* icmp should have updated the destination cache entry */
1473df80d93SArnaldo Carvalho de Melo 		dst = __sk_dst_check(sk, np->dst_cookie);
1483df80d93SArnaldo Carvalho de Melo 		if (dst == NULL) {
1493df80d93SArnaldo Carvalho de Melo 			struct inet_sock *inet = inet_sk(sk);
1503df80d93SArnaldo Carvalho de Melo 			struct flowi fl;
1513df80d93SArnaldo Carvalho de Melo 
1523df80d93SArnaldo Carvalho de Melo 			/* BUGGG_FUTURE: Again, it is not clear how
1533df80d93SArnaldo Carvalho de Melo 			   to handle rthdr case. Ignore this complexity
1543df80d93SArnaldo Carvalho de Melo 			   for now.
1553df80d93SArnaldo Carvalho de Melo 			 */
1563df80d93SArnaldo Carvalho de Melo 			memset(&fl, 0, sizeof(fl));
1573df80d93SArnaldo Carvalho de Melo 			fl.proto = IPPROTO_DCCP;
1583df80d93SArnaldo Carvalho de Melo 			ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
1593df80d93SArnaldo Carvalho de Melo 			ipv6_addr_copy(&fl.fl6_src, &np->saddr);
1603df80d93SArnaldo Carvalho de Melo 			fl.oif = sk->sk_bound_dev_if;
161c720c7e8SEric Dumazet 			fl.fl_ip_dport = inet->inet_dport;
162c720c7e8SEric Dumazet 			fl.fl_ip_sport = inet->inet_sport;
163beb8d13bSVenkat Yekkirala 			security_sk_classify_flow(sk, &fl);
1643df80d93SArnaldo Carvalho de Melo 
16545329e71SArnaldo Carvalho de Melo 			err = ip6_dst_lookup(sk, &dst, &fl);
16645329e71SArnaldo Carvalho de Melo 			if (err) {
1673df80d93SArnaldo Carvalho de Melo 				sk->sk_err_soft = -err;
1683df80d93SArnaldo Carvalho de Melo 				goto out;
1693df80d93SArnaldo Carvalho de Melo 			}
1703df80d93SArnaldo Carvalho de Melo 
17152479b62SAlexey Dobriyan 			err = xfrm_lookup(net, &dst, &fl, sk, 0);
17245329e71SArnaldo Carvalho de Melo 			if (err < 0) {
1733df80d93SArnaldo Carvalho de Melo 				sk->sk_err_soft = -err;
1743df80d93SArnaldo Carvalho de Melo 				goto out;
1753df80d93SArnaldo Carvalho de Melo 			}
1763df80d93SArnaldo Carvalho de Melo 		} else
1773df80d93SArnaldo Carvalho de Melo 			dst_hold(dst);
1783df80d93SArnaldo Carvalho de Melo 
179d83d8461SArnaldo Carvalho de Melo 		if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst)) {
1803df80d93SArnaldo Carvalho de Melo 			dccp_sync_mss(sk, dst_mtu(dst));
1813df80d93SArnaldo Carvalho de Melo 		} /* else let the usual retransmit timer handle it */
1823df80d93SArnaldo Carvalho de Melo 		dst_release(dst);
1833df80d93SArnaldo Carvalho de Melo 		goto out;
1843df80d93SArnaldo Carvalho de Melo 	}
1853df80d93SArnaldo Carvalho de Melo 
1863df80d93SArnaldo Carvalho de Melo 	icmpv6_err_convert(type, code, &err);
1873df80d93SArnaldo Carvalho de Melo 
1883df80d93SArnaldo Carvalho de Melo 	/* Might be for an request_sock */
1893df80d93SArnaldo Carvalho de Melo 	switch (sk->sk_state) {
1903df80d93SArnaldo Carvalho de Melo 		struct request_sock *req, **prev;
1913df80d93SArnaldo Carvalho de Melo 	case DCCP_LISTEN:
1923df80d93SArnaldo Carvalho de Melo 		if (sock_owned_by_user(sk))
1933df80d93SArnaldo Carvalho de Melo 			goto out;
1943df80d93SArnaldo Carvalho de Melo 
1953df80d93SArnaldo Carvalho de Melo 		req = inet6_csk_search_req(sk, &prev, dh->dccph_dport,
1963df80d93SArnaldo Carvalho de Melo 					   &hdr->daddr, &hdr->saddr,
1973df80d93SArnaldo Carvalho de Melo 					   inet6_iif(skb));
19845329e71SArnaldo Carvalho de Melo 		if (req == NULL)
1993df80d93SArnaldo Carvalho de Melo 			goto out;
2003df80d93SArnaldo Carvalho de Melo 
20145329e71SArnaldo Carvalho de Melo 		/*
20245329e71SArnaldo Carvalho de Melo 		 * ICMPs are not backlogged, hence we cannot get an established
20345329e71SArnaldo Carvalho de Melo 		 * socket here.
2043df80d93SArnaldo Carvalho de Melo 		 */
205547b792cSIlpo Järvinen 		WARN_ON(req->sk != NULL);
2063df80d93SArnaldo Carvalho de Melo 
2073df80d93SArnaldo Carvalho de Melo 		if (seq != dccp_rsk(req)->dreq_iss) {
208de0744afSPavel Emelyanov 			NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
2093df80d93SArnaldo Carvalho de Melo 			goto out;
2103df80d93SArnaldo Carvalho de Melo 		}
2113df80d93SArnaldo Carvalho de Melo 
2123df80d93SArnaldo Carvalho de Melo 		inet_csk_reqsk_queue_drop(sk, req, prev);
2133df80d93SArnaldo Carvalho de Melo 		goto out;
2143df80d93SArnaldo Carvalho de Melo 
2153df80d93SArnaldo Carvalho de Melo 	case DCCP_REQUESTING:
2163df80d93SArnaldo Carvalho de Melo 	case DCCP_RESPOND:  /* Cannot happen.
2173df80d93SArnaldo Carvalho de Melo 			       It can, it SYNs are crossed. --ANK */
2183df80d93SArnaldo Carvalho de Melo 		if (!sock_owned_by_user(sk)) {
2193df80d93SArnaldo Carvalho de Melo 			DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS);
2203df80d93SArnaldo Carvalho de Melo 			sk->sk_err = err;
2213df80d93SArnaldo Carvalho de Melo 			/*
2223df80d93SArnaldo Carvalho de Melo 			 * Wake people up to see the error
2233df80d93SArnaldo Carvalho de Melo 			 * (see connect in sock.c)
2243df80d93SArnaldo Carvalho de Melo 			 */
2253df80d93SArnaldo Carvalho de Melo 			sk->sk_error_report(sk);
2263df80d93SArnaldo Carvalho de Melo 			dccp_done(sk);
2273df80d93SArnaldo Carvalho de Melo 		} else
2283df80d93SArnaldo Carvalho de Melo 			sk->sk_err_soft = err;
2293df80d93SArnaldo Carvalho de Melo 		goto out;
2303df80d93SArnaldo Carvalho de Melo 	}
2313df80d93SArnaldo Carvalho de Melo 
2323df80d93SArnaldo Carvalho de Melo 	if (!sock_owned_by_user(sk) && np->recverr) {
2333df80d93SArnaldo Carvalho de Melo 		sk->sk_err = err;
2343df80d93SArnaldo Carvalho de Melo 		sk->sk_error_report(sk);
2353df80d93SArnaldo Carvalho de Melo 	} else
2363df80d93SArnaldo Carvalho de Melo 		sk->sk_err_soft = err;
2373df80d93SArnaldo Carvalho de Melo 
2383df80d93SArnaldo Carvalho de Melo out:
2393df80d93SArnaldo Carvalho de Melo 	bh_unlock_sock(sk);
2403df80d93SArnaldo Carvalho de Melo 	sock_put(sk);
2413df80d93SArnaldo Carvalho de Melo }
2423df80d93SArnaldo Carvalho de Melo 
2433df80d93SArnaldo Carvalho de Melo 
244e6b4d113SWilliam Allen Simpson static int dccp_v6_send_response(struct sock *sk, struct request_sock *req,
245e6b4d113SWilliam Allen Simpson 				 struct request_values *rv_unused)
2463df80d93SArnaldo Carvalho de Melo {
2473df80d93SArnaldo Carvalho de Melo 	struct inet6_request_sock *ireq6 = inet6_rsk(req);
2483df80d93SArnaldo Carvalho de Melo 	struct ipv6_pinfo *np = inet6_sk(sk);
2493df80d93SArnaldo Carvalho de Melo 	struct sk_buff *skb;
2503df80d93SArnaldo Carvalho de Melo 	struct ipv6_txoptions *opt = NULL;
25120c59de2SArnaud Ebalard 	struct in6_addr *final_p, final;
2523df80d93SArnaldo Carvalho de Melo 	struct flowi fl;
2533df80d93SArnaldo Carvalho de Melo 	int err = -1;
254fd80eb94SDenis V. Lunev 	struct dst_entry *dst;
2553df80d93SArnaldo Carvalho de Melo 
2563df80d93SArnaldo Carvalho de Melo 	memset(&fl, 0, sizeof(fl));
2573df80d93SArnaldo Carvalho de Melo 	fl.proto = IPPROTO_DCCP;
2583df80d93SArnaldo Carvalho de Melo 	ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
2593df80d93SArnaldo Carvalho de Melo 	ipv6_addr_copy(&fl.fl6_src, &ireq6->loc_addr);
2603df80d93SArnaldo Carvalho de Melo 	fl.fl6_flowlabel = 0;
2613df80d93SArnaldo Carvalho de Melo 	fl.oif = ireq6->iif;
2623df80d93SArnaldo Carvalho de Melo 	fl.fl_ip_dport = inet_rsk(req)->rmt_port;
263944f7502SGerrit Renker 	fl.fl_ip_sport = inet_rsk(req)->loc_port;
2644237c75cSVenkat Yekkirala 	security_req_classify_flow(req, &fl);
2653df80d93SArnaldo Carvalho de Melo 
2663df80d93SArnaldo Carvalho de Melo 	opt = np->opt;
2673df80d93SArnaldo Carvalho de Melo 
26820c59de2SArnaud Ebalard 	final_p = fl6_update_dst(&fl, opt, &final);
2693df80d93SArnaldo Carvalho de Melo 
2703df80d93SArnaldo Carvalho de Melo 	err = ip6_dst_lookup(sk, &dst, &fl);
2713df80d93SArnaldo Carvalho de Melo 	if (err)
2723df80d93SArnaldo Carvalho de Melo 		goto done;
27345329e71SArnaldo Carvalho de Melo 
2743df80d93SArnaldo Carvalho de Melo 	if (final_p)
2753df80d93SArnaldo Carvalho de Melo 		ipv6_addr_copy(&fl.fl6_dst, final_p);
27645329e71SArnaldo Carvalho de Melo 
27752479b62SAlexey Dobriyan 	err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0);
27845329e71SArnaldo Carvalho de Melo 	if (err < 0)
2793df80d93SArnaldo Carvalho de Melo 		goto done;
2803df80d93SArnaldo Carvalho de Melo 
2813df80d93SArnaldo Carvalho de Melo 	skb = dccp_make_response(sk, dst, req);
2823df80d93SArnaldo Carvalho de Melo 	if (skb != NULL) {
2833df80d93SArnaldo Carvalho de Melo 		struct dccp_hdr *dh = dccp_hdr(skb);
28445329e71SArnaldo Carvalho de Melo 
2856f4e5fffSGerrit Renker 		dh->dccph_checksum = dccp_v6_csum_finish(skb,
2863df80d93SArnaldo Carvalho de Melo 							 &ireq6->loc_addr,
2876f4e5fffSGerrit Renker 							 &ireq6->rmt_addr);
2883df80d93SArnaldo Carvalho de Melo 		ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
2894e15ed4dSShan Wei 		err = ip6_xmit(sk, skb, &fl, opt);
290b9df3cb8SGerrit Renker 		err = net_xmit_eval(err);
2913df80d93SArnaldo Carvalho de Melo 	}
2923df80d93SArnaldo Carvalho de Melo 
2933df80d93SArnaldo Carvalho de Melo done:
29445329e71SArnaldo Carvalho de Melo 	if (opt != NULL && opt != np->opt)
2953df80d93SArnaldo Carvalho de Melo 		sock_kfree_s(sk, opt, opt->tot_len);
2960cbd7825SDavid S. Miller 	dst_release(dst);
2973df80d93SArnaldo Carvalho de Melo 	return err;
2983df80d93SArnaldo Carvalho de Melo }
2993df80d93SArnaldo Carvalho de Melo 
3003df80d93SArnaldo Carvalho de Melo static void dccp_v6_reqsk_destructor(struct request_sock *req)
3013df80d93SArnaldo Carvalho de Melo {
302d99a7bd2SGerrit Renker 	dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg);
3033df80d93SArnaldo Carvalho de Melo 	if (inet6_rsk(req)->pktopts != NULL)
3043df80d93SArnaldo Carvalho de Melo 		kfree_skb(inet6_rsk(req)->pktopts);
3053df80d93SArnaldo Carvalho de Melo }
3063df80d93SArnaldo Carvalho de Melo 
307cfb6eeb4SYOSHIFUJI Hideaki static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb)
3083df80d93SArnaldo Carvalho de Melo {
3090660e03fSArnaldo Carvalho de Melo 	struct ipv6hdr *rxip6h;
3103df80d93SArnaldo Carvalho de Melo 	struct sk_buff *skb;
3113df80d93SArnaldo Carvalho de Melo 	struct flowi fl;
312adf30907SEric Dumazet 	struct net *net = dev_net(skb_dst(rxskb)->dev);
313334527d3SPavel Emelyanov 	struct sock *ctl_sk = net->dccp.v6_ctl_sk;
314adf30907SEric Dumazet 	struct dst_entry *dst;
3153df80d93SArnaldo Carvalho de Melo 
316e356d37aSGerrit Renker 	if (dccp_hdr(rxskb)->dccph_type == DCCP_PKT_RESET)
3173df80d93SArnaldo Carvalho de Melo 		return;
3183df80d93SArnaldo Carvalho de Melo 
3193df80d93SArnaldo Carvalho de Melo 	if (!ipv6_unicast_destination(rxskb))
3203df80d93SArnaldo Carvalho de Melo 		return;
3213df80d93SArnaldo Carvalho de Melo 
32202047741SPavel Emelyanov 	skb = dccp_ctl_make_reset(ctl_sk, rxskb);
3233df80d93SArnaldo Carvalho de Melo 	if (skb == NULL)
3243df80d93SArnaldo Carvalho de Melo 		return;
3253df80d93SArnaldo Carvalho de Melo 
3260660e03fSArnaldo Carvalho de Melo 	rxip6h = ipv6_hdr(rxskb);
327e356d37aSGerrit Renker 	dccp_hdr(skb)->dccph_checksum = dccp_v6_csum_finish(skb, &rxip6h->saddr,
3280660e03fSArnaldo Carvalho de Melo 							    &rxip6h->daddr);
3296f4e5fffSGerrit Renker 
3303df80d93SArnaldo Carvalho de Melo 	memset(&fl, 0, sizeof(fl));
3310660e03fSArnaldo Carvalho de Melo 	ipv6_addr_copy(&fl.fl6_dst, &rxip6h->saddr);
3320660e03fSArnaldo Carvalho de Melo 	ipv6_addr_copy(&fl.fl6_src, &rxip6h->daddr);
3336f4e5fffSGerrit Renker 
3343df80d93SArnaldo Carvalho de Melo 	fl.proto = IPPROTO_DCCP;
3353df80d93SArnaldo Carvalho de Melo 	fl.oif = inet6_iif(rxskb);
336e356d37aSGerrit Renker 	fl.fl_ip_dport = dccp_hdr(skb)->dccph_dport;
337e356d37aSGerrit Renker 	fl.fl_ip_sport = dccp_hdr(skb)->dccph_sport;
338beb8d13bSVenkat Yekkirala 	security_skb_classify_flow(rxskb, &fl);
3393df80d93SArnaldo Carvalho de Melo 
3403df80d93SArnaldo Carvalho de Melo 	/* sk = NULL, but it is safe for now. RST socket required. */
341adf30907SEric Dumazet 	if (!ip6_dst_lookup(ctl_sk, &dst, &fl)) {
342adf30907SEric Dumazet 		if (xfrm_lookup(net, &dst, &fl, NULL, 0) >= 0) {
343adf30907SEric Dumazet 			skb_dst_set(skb, dst);
3444e15ed4dSShan Wei 			ip6_xmit(ctl_sk, skb, &fl, NULL);
3453df80d93SArnaldo Carvalho de Melo 			DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS);
3463df80d93SArnaldo Carvalho de Melo 			DCCP_INC_STATS_BH(DCCP_MIB_OUTRSTS);
3473df80d93SArnaldo Carvalho de Melo 			return;
3483df80d93SArnaldo Carvalho de Melo 		}
3493df80d93SArnaldo Carvalho de Melo 	}
3503df80d93SArnaldo Carvalho de Melo 
3513df80d93SArnaldo Carvalho de Melo 	kfree_skb(skb);
3523df80d93SArnaldo Carvalho de Melo }
3533df80d93SArnaldo Carvalho de Melo 
35473c9e02cSGerrit Renker static struct request_sock_ops dccp6_request_sock_ops = {
35573c9e02cSGerrit Renker 	.family		= AF_INET6,
35673c9e02cSGerrit Renker 	.obj_size	= sizeof(struct dccp6_request_sock),
35773c9e02cSGerrit Renker 	.rtx_syn_ack	= dccp_v6_send_response,
35873c9e02cSGerrit Renker 	.send_ack	= dccp_reqsk_send_ack,
35973c9e02cSGerrit Renker 	.destructor	= dccp_v6_reqsk_destructor,
36073c9e02cSGerrit Renker 	.send_reset	= dccp_v6_ctl_send_reset,
36173c9e02cSGerrit Renker };
36273c9e02cSGerrit Renker 
3633df80d93SArnaldo Carvalho de Melo static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
3643df80d93SArnaldo Carvalho de Melo {
3653df80d93SArnaldo Carvalho de Melo 	const struct dccp_hdr *dh = dccp_hdr(skb);
3660660e03fSArnaldo Carvalho de Melo 	const struct ipv6hdr *iph = ipv6_hdr(skb);
3673df80d93SArnaldo Carvalho de Melo 	struct sock *nsk;
3683df80d93SArnaldo Carvalho de Melo 	struct request_sock **prev;
3693df80d93SArnaldo Carvalho de Melo 	/* Find possible connection requests. */
3703df80d93SArnaldo Carvalho de Melo 	struct request_sock *req = inet6_csk_search_req(sk, &prev,
3713df80d93SArnaldo Carvalho de Melo 							dh->dccph_sport,
3723df80d93SArnaldo Carvalho de Melo 							&iph->saddr,
3733df80d93SArnaldo Carvalho de Melo 							&iph->daddr,
3743df80d93SArnaldo Carvalho de Melo 							inet6_iif(skb));
3753df80d93SArnaldo Carvalho de Melo 	if (req != NULL)
3763df80d93SArnaldo Carvalho de Melo 		return dccp_check_req(sk, skb, req, prev);
3773df80d93SArnaldo Carvalho de Melo 
378671a1c74SPavel Emelyanov 	nsk = __inet6_lookup_established(sock_net(sk), &dccp_hashinfo,
3793df80d93SArnaldo Carvalho de Melo 					 &iph->saddr, dh->dccph_sport,
3803df80d93SArnaldo Carvalho de Melo 					 &iph->daddr, ntohs(dh->dccph_dport),
3813df80d93SArnaldo Carvalho de Melo 					 inet6_iif(skb));
3823df80d93SArnaldo Carvalho de Melo 	if (nsk != NULL) {
3833df80d93SArnaldo Carvalho de Melo 		if (nsk->sk_state != DCCP_TIME_WAIT) {
3843df80d93SArnaldo Carvalho de Melo 			bh_lock_sock(nsk);
3853df80d93SArnaldo Carvalho de Melo 			return nsk;
3863df80d93SArnaldo Carvalho de Melo 		}
3879469c7b4SYOSHIFUJI Hideaki 		inet_twsk_put(inet_twsk(nsk));
3883df80d93SArnaldo Carvalho de Melo 		return NULL;
3893df80d93SArnaldo Carvalho de Melo 	}
3903df80d93SArnaldo Carvalho de Melo 
3913df80d93SArnaldo Carvalho de Melo 	return sk;
3923df80d93SArnaldo Carvalho de Melo }
3933df80d93SArnaldo Carvalho de Melo 
3943df80d93SArnaldo Carvalho de Melo static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
3953df80d93SArnaldo Carvalho de Melo {
3963df80d93SArnaldo Carvalho de Melo 	struct request_sock *req;
3973df80d93SArnaldo Carvalho de Melo 	struct dccp_request_sock *dreq;
3983df80d93SArnaldo Carvalho de Melo 	struct inet6_request_sock *ireq6;
3993df80d93SArnaldo Carvalho de Melo 	struct ipv6_pinfo *np = inet6_sk(sk);
40060fe62e7SAndrea Bittau 	const __be32 service = dccp_hdr_request(skb)->dccph_req_service;
4013df80d93SArnaldo Carvalho de Melo 	struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
4023df80d93SArnaldo Carvalho de Melo 
4033df80d93SArnaldo Carvalho de Melo 	if (skb->protocol == htons(ETH_P_IP))
4043df80d93SArnaldo Carvalho de Melo 		return dccp_v4_conn_request(sk, skb);
4053df80d93SArnaldo Carvalho de Melo 
4063df80d93SArnaldo Carvalho de Melo 	if (!ipv6_unicast_destination(skb))
4074a5409a5SGerrit Renker 		return 0;	/* discard, don't send a reset here */
4083df80d93SArnaldo Carvalho de Melo 
4093df80d93SArnaldo Carvalho de Melo 	if (dccp_bad_service_code(sk, service)) {
4104a5409a5SGerrit Renker 		dcb->dccpd_reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE;
4113df80d93SArnaldo Carvalho de Melo 		goto drop;
4123df80d93SArnaldo Carvalho de Melo 	}
4133df80d93SArnaldo Carvalho de Melo 	/*
4143df80d93SArnaldo Carvalho de Melo 	 * There are no SYN attacks on IPv6, yet...
4153df80d93SArnaldo Carvalho de Melo 	 */
4164a5409a5SGerrit Renker 	dcb->dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY;
4173df80d93SArnaldo Carvalho de Melo 	if (inet_csk_reqsk_queue_is_full(sk))
4183df80d93SArnaldo Carvalho de Melo 		goto drop;
4193df80d93SArnaldo Carvalho de Melo 
4203df80d93SArnaldo Carvalho de Melo 	if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
4213df80d93SArnaldo Carvalho de Melo 		goto drop;
4223df80d93SArnaldo Carvalho de Melo 
42382709531SGerrit Renker 	req = inet6_reqsk_alloc(&dccp6_request_sock_ops);
4243df80d93SArnaldo Carvalho de Melo 	if (req == NULL)
4253df80d93SArnaldo Carvalho de Melo 		goto drop;
4263df80d93SArnaldo Carvalho de Melo 
427ac75773cSGerrit Renker 	if (dccp_reqsk_init(req, dccp_sk(sk), skb))
428ac75773cSGerrit Renker 		goto drop_and_free;
4293df80d93SArnaldo Carvalho de Melo 
4308b819412SGerrit Renker 	dreq = dccp_rsk(req);
4318b819412SGerrit Renker 	if (dccp_parse_options(sk, dreq, skb))
4328b819412SGerrit Renker 		goto drop_and_free;
4338b819412SGerrit Renker 
4344237c75cSVenkat Yekkirala 	if (security_inet_conn_request(sk, skb, req))
4354237c75cSVenkat Yekkirala 		goto drop_and_free;
4364237c75cSVenkat Yekkirala 
4373df80d93SArnaldo Carvalho de Melo 	ireq6 = inet6_rsk(req);
4380660e03fSArnaldo Carvalho de Melo 	ipv6_addr_copy(&ireq6->rmt_addr, &ipv6_hdr(skb)->saddr);
4390660e03fSArnaldo Carvalho de Melo 	ipv6_addr_copy(&ireq6->loc_addr, &ipv6_hdr(skb)->daddr);
4403df80d93SArnaldo Carvalho de Melo 
4413df80d93SArnaldo Carvalho de Melo 	if (ipv6_opt_accepted(sk, skb) ||
4423df80d93SArnaldo Carvalho de Melo 	    np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
4433df80d93SArnaldo Carvalho de Melo 	    np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
4443df80d93SArnaldo Carvalho de Melo 		atomic_inc(&skb->users);
4453df80d93SArnaldo Carvalho de Melo 		ireq6->pktopts = skb;
4463df80d93SArnaldo Carvalho de Melo 	}
4473df80d93SArnaldo Carvalho de Melo 	ireq6->iif = sk->sk_bound_dev_if;
4483df80d93SArnaldo Carvalho de Melo 
4493df80d93SArnaldo Carvalho de Melo 	/* So that link locals have meaning */
4503df80d93SArnaldo Carvalho de Melo 	if (!sk->sk_bound_dev_if &&
4513df80d93SArnaldo Carvalho de Melo 	    ipv6_addr_type(&ireq6->rmt_addr) & IPV6_ADDR_LINKLOCAL)
4523df80d93SArnaldo Carvalho de Melo 		ireq6->iif = inet6_iif(skb);
4533df80d93SArnaldo Carvalho de Melo 
4543df80d93SArnaldo Carvalho de Melo 	/*
4553df80d93SArnaldo Carvalho de Melo 	 * Step 3: Process LISTEN state
4563df80d93SArnaldo Carvalho de Melo 	 *
4573df80d93SArnaldo Carvalho de Melo 	 *   Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie
4583df80d93SArnaldo Carvalho de Melo 	 *
4593df80d93SArnaldo Carvalho de Melo 	 *   In fact we defer setting S.GSR, S.SWL, S.SWH to
4603df80d93SArnaldo Carvalho de Melo 	 *   dccp_create_openreq_child.
4613df80d93SArnaldo Carvalho de Melo 	 */
4623df80d93SArnaldo Carvalho de Melo 	dreq->dreq_isr	   = dcb->dccpd_seq;
463865e9022SGerrit Renker 	dreq->dreq_iss	   = dccp_v6_init_sequence(skb);
4643df80d93SArnaldo Carvalho de Melo 	dreq->dreq_service = service;
4653df80d93SArnaldo Carvalho de Melo 
466e6b4d113SWilliam Allen Simpson 	if (dccp_v6_send_response(sk, req, NULL))
4673df80d93SArnaldo Carvalho de Melo 		goto drop_and_free;
4683df80d93SArnaldo Carvalho de Melo 
4693df80d93SArnaldo Carvalho de Melo 	inet6_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
4703df80d93SArnaldo Carvalho de Melo 	return 0;
4713df80d93SArnaldo Carvalho de Melo 
4723df80d93SArnaldo Carvalho de Melo drop_and_free:
4733df80d93SArnaldo Carvalho de Melo 	reqsk_free(req);
4743df80d93SArnaldo Carvalho de Melo drop:
4753df80d93SArnaldo Carvalho de Melo 	DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS);
4763df80d93SArnaldo Carvalho de Melo 	return -1;
4773df80d93SArnaldo Carvalho de Melo }
4783df80d93SArnaldo Carvalho de Melo 
4793df80d93SArnaldo Carvalho de Melo static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
4803df80d93SArnaldo Carvalho de Melo 					      struct sk_buff *skb,
4813df80d93SArnaldo Carvalho de Melo 					      struct request_sock *req,
4823df80d93SArnaldo Carvalho de Melo 					      struct dst_entry *dst)
4833df80d93SArnaldo Carvalho de Melo {
4843df80d93SArnaldo Carvalho de Melo 	struct inet6_request_sock *ireq6 = inet6_rsk(req);
4853df80d93SArnaldo Carvalho de Melo 	struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
4863df80d93SArnaldo Carvalho de Melo 	struct inet_sock *newinet;
4873df80d93SArnaldo Carvalho de Melo 	struct dccp_sock *newdp;
4883df80d93SArnaldo Carvalho de Melo 	struct dccp6_sock *newdp6;
4893df80d93SArnaldo Carvalho de Melo 	struct sock *newsk;
4903df80d93SArnaldo Carvalho de Melo 	struct ipv6_txoptions *opt;
4913df80d93SArnaldo Carvalho de Melo 
4923df80d93SArnaldo Carvalho de Melo 	if (skb->protocol == htons(ETH_P_IP)) {
4933df80d93SArnaldo Carvalho de Melo 		/*
4943df80d93SArnaldo Carvalho de Melo 		 *	v6 mapped
4953df80d93SArnaldo Carvalho de Melo 		 */
4963df80d93SArnaldo Carvalho de Melo 		newsk = dccp_v4_request_recv_sock(sk, skb, req, dst);
4973df80d93SArnaldo Carvalho de Melo 		if (newsk == NULL)
4983df80d93SArnaldo Carvalho de Melo 			return NULL;
4993df80d93SArnaldo Carvalho de Melo 
5003df80d93SArnaldo Carvalho de Melo 		newdp6 = (struct dccp6_sock *)newsk;
5013df80d93SArnaldo Carvalho de Melo 		newdp = dccp_sk(newsk);
5023df80d93SArnaldo Carvalho de Melo 		newinet = inet_sk(newsk);
5033df80d93SArnaldo Carvalho de Melo 		newinet->pinet6 = &newdp6->inet6;
5043df80d93SArnaldo Carvalho de Melo 		newnp = inet6_sk(newsk);
5053df80d93SArnaldo Carvalho de Melo 
5063df80d93SArnaldo Carvalho de Melo 		memcpy(newnp, np, sizeof(struct ipv6_pinfo));
5073df80d93SArnaldo Carvalho de Melo 
508c720c7e8SEric Dumazet 		ipv6_addr_set_v4mapped(newinet->inet_daddr, &newnp->daddr);
5093df80d93SArnaldo Carvalho de Melo 
510c720c7e8SEric Dumazet 		ipv6_addr_set_v4mapped(newinet->inet_saddr, &newnp->saddr);
5113df80d93SArnaldo Carvalho de Melo 
5123df80d93SArnaldo Carvalho de Melo 		ipv6_addr_copy(&newnp->rcv_saddr, &newnp->saddr);
5133df80d93SArnaldo Carvalho de Melo 
5143df80d93SArnaldo Carvalho de Melo 		inet_csk(newsk)->icsk_af_ops = &dccp_ipv6_mapped;
5153df80d93SArnaldo Carvalho de Melo 		newsk->sk_backlog_rcv = dccp_v4_do_rcv;
5163df80d93SArnaldo Carvalho de Melo 		newnp->pktoptions  = NULL;
5173df80d93SArnaldo Carvalho de Melo 		newnp->opt	   = NULL;
5183df80d93SArnaldo Carvalho de Melo 		newnp->mcast_oif   = inet6_iif(skb);
5190660e03fSArnaldo Carvalho de Melo 		newnp->mcast_hops  = ipv6_hdr(skb)->hop_limit;
5203df80d93SArnaldo Carvalho de Melo 
5213df80d93SArnaldo Carvalho de Melo 		/*
5223df80d93SArnaldo Carvalho de Melo 		 * No need to charge this sock to the relevant IPv6 refcnt debug socks count
5233df80d93SArnaldo Carvalho de Melo 		 * here, dccp_create_openreq_child now does this for us, see the comment in
5243df80d93SArnaldo Carvalho de Melo 		 * that function for the gory details. -acme
5253df80d93SArnaldo Carvalho de Melo 		 */
5263df80d93SArnaldo Carvalho de Melo 
5273df80d93SArnaldo Carvalho de Melo 		/* It is tricky place. Until this moment IPv4 tcp
5283df80d93SArnaldo Carvalho de Melo 		   worked with IPv6 icsk.icsk_af_ops.
5293df80d93SArnaldo Carvalho de Melo 		   Sync it now.
5303df80d93SArnaldo Carvalho de Melo 		 */
531d83d8461SArnaldo Carvalho de Melo 		dccp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
5323df80d93SArnaldo Carvalho de Melo 
5333df80d93SArnaldo Carvalho de Melo 		return newsk;
5343df80d93SArnaldo Carvalho de Melo 	}
5353df80d93SArnaldo Carvalho de Melo 
5363df80d93SArnaldo Carvalho de Melo 	opt = np->opt;
5373df80d93SArnaldo Carvalho de Melo 
5383df80d93SArnaldo Carvalho de Melo 	if (sk_acceptq_is_full(sk))
5393df80d93SArnaldo Carvalho de Melo 		goto out_overflow;
5403df80d93SArnaldo Carvalho de Melo 
5413df80d93SArnaldo Carvalho de Melo 	if (dst == NULL) {
54220c59de2SArnaud Ebalard 		struct in6_addr *final_p, final;
5433df80d93SArnaldo Carvalho de Melo 		struct flowi fl;
5443df80d93SArnaldo Carvalho de Melo 
5453df80d93SArnaldo Carvalho de Melo 		memset(&fl, 0, sizeof(fl));
5463df80d93SArnaldo Carvalho de Melo 		fl.proto = IPPROTO_DCCP;
5473df80d93SArnaldo Carvalho de Melo 		ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
54820c59de2SArnaud Ebalard 		final_p = fl6_update_dst(&fl, opt, &final);
5493df80d93SArnaldo Carvalho de Melo 		ipv6_addr_copy(&fl.fl6_src, &ireq6->loc_addr);
5503df80d93SArnaldo Carvalho de Melo 		fl.oif = sk->sk_bound_dev_if;
5513df80d93SArnaldo Carvalho de Melo 		fl.fl_ip_dport = inet_rsk(req)->rmt_port;
552944f7502SGerrit Renker 		fl.fl_ip_sport = inet_rsk(req)->loc_port;
553beb8d13bSVenkat Yekkirala 		security_sk_classify_flow(sk, &fl);
5543df80d93SArnaldo Carvalho de Melo 
5553df80d93SArnaldo Carvalho de Melo 		if (ip6_dst_lookup(sk, &dst, &fl))
5563df80d93SArnaldo Carvalho de Melo 			goto out;
5573df80d93SArnaldo Carvalho de Melo 
5583df80d93SArnaldo Carvalho de Melo 		if (final_p)
5593df80d93SArnaldo Carvalho de Melo 			ipv6_addr_copy(&fl.fl6_dst, final_p);
5603df80d93SArnaldo Carvalho de Melo 
56152479b62SAlexey Dobriyan 		if ((xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0)
5623df80d93SArnaldo Carvalho de Melo 			goto out;
5633df80d93SArnaldo Carvalho de Melo 	}
5643df80d93SArnaldo Carvalho de Melo 
5653df80d93SArnaldo Carvalho de Melo 	newsk = dccp_create_openreq_child(sk, req, skb);
5663df80d93SArnaldo Carvalho de Melo 	if (newsk == NULL)
567*093d2823SBalazs Scheidler 		goto out_nonewsk;
5683df80d93SArnaldo Carvalho de Melo 
5693df80d93SArnaldo Carvalho de Melo 	/*
5703df80d93SArnaldo Carvalho de Melo 	 * No need to charge this sock to the relevant IPv6 refcnt debug socks
5713df80d93SArnaldo Carvalho de Melo 	 * count here, dccp_create_openreq_child now does this for us, see the
5723df80d93SArnaldo Carvalho de Melo 	 * comment in that function for the gory details. -acme
5733df80d93SArnaldo Carvalho de Melo 	 */
5743df80d93SArnaldo Carvalho de Melo 
5758e1ef0a9SYOSHIFUJI Hideaki 	__ip6_dst_store(newsk, dst, NULL, NULL);
57645329e71SArnaldo Carvalho de Melo 	newsk->sk_route_caps = dst->dev->features & ~(NETIF_F_IP_CSUM |
57745329e71SArnaldo Carvalho de Melo 						      NETIF_F_TSO);
5783df80d93SArnaldo Carvalho de Melo 	newdp6 = (struct dccp6_sock *)newsk;
5793df80d93SArnaldo Carvalho de Melo 	newinet = inet_sk(newsk);
5803df80d93SArnaldo Carvalho de Melo 	newinet->pinet6 = &newdp6->inet6;
5813df80d93SArnaldo Carvalho de Melo 	newdp = dccp_sk(newsk);
5823df80d93SArnaldo Carvalho de Melo 	newnp = inet6_sk(newsk);
5833df80d93SArnaldo Carvalho de Melo 
5843df80d93SArnaldo Carvalho de Melo 	memcpy(newnp, np, sizeof(struct ipv6_pinfo));
5853df80d93SArnaldo Carvalho de Melo 
5863df80d93SArnaldo Carvalho de Melo 	ipv6_addr_copy(&newnp->daddr, &ireq6->rmt_addr);
5873df80d93SArnaldo Carvalho de Melo 	ipv6_addr_copy(&newnp->saddr, &ireq6->loc_addr);
5883df80d93SArnaldo Carvalho de Melo 	ipv6_addr_copy(&newnp->rcv_saddr, &ireq6->loc_addr);
5893df80d93SArnaldo Carvalho de Melo 	newsk->sk_bound_dev_if = ireq6->iif;
5903df80d93SArnaldo Carvalho de Melo 
5913df80d93SArnaldo Carvalho de Melo 	/* Now IPv6 options...
5923df80d93SArnaldo Carvalho de Melo 
5933df80d93SArnaldo Carvalho de Melo 	   First: no IPv4 options.
5943df80d93SArnaldo Carvalho de Melo 	 */
5953df80d93SArnaldo Carvalho de Melo 	newinet->opt = NULL;
5963df80d93SArnaldo Carvalho de Melo 
5973df80d93SArnaldo Carvalho de Melo 	/* Clone RX bits */
5983df80d93SArnaldo Carvalho de Melo 	newnp->rxopt.all = np->rxopt.all;
5993df80d93SArnaldo Carvalho de Melo 
6003df80d93SArnaldo Carvalho de Melo 	/* Clone pktoptions received with SYN */
6013df80d93SArnaldo Carvalho de Melo 	newnp->pktoptions = NULL;
6023df80d93SArnaldo Carvalho de Melo 	if (ireq6->pktopts != NULL) {
6033df80d93SArnaldo Carvalho de Melo 		newnp->pktoptions = skb_clone(ireq6->pktopts, GFP_ATOMIC);
6043df80d93SArnaldo Carvalho de Melo 		kfree_skb(ireq6->pktopts);
6053df80d93SArnaldo Carvalho de Melo 		ireq6->pktopts = NULL;
6063df80d93SArnaldo Carvalho de Melo 		if (newnp->pktoptions)
6073df80d93SArnaldo Carvalho de Melo 			skb_set_owner_r(newnp->pktoptions, newsk);
6083df80d93SArnaldo Carvalho de Melo 	}
6093df80d93SArnaldo Carvalho de Melo 	newnp->opt	  = NULL;
6103df80d93SArnaldo Carvalho de Melo 	newnp->mcast_oif  = inet6_iif(skb);
6110660e03fSArnaldo Carvalho de Melo 	newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
6123df80d93SArnaldo Carvalho de Melo 
61345329e71SArnaldo Carvalho de Melo 	/*
61445329e71SArnaldo Carvalho de Melo 	 * Clone native IPv6 options from listening socket (if any)
61545329e71SArnaldo Carvalho de Melo 	 *
61645329e71SArnaldo Carvalho de Melo 	 * Yes, keeping reference count would be much more clever, but we make
61745329e71SArnaldo Carvalho de Melo 	 * one more one thing there: reattach optmem to newsk.
6183df80d93SArnaldo Carvalho de Melo 	 */
61945329e71SArnaldo Carvalho de Melo 	if (opt != NULL) {
6203df80d93SArnaldo Carvalho de Melo 		newnp->opt = ipv6_dup_options(newsk, opt);
6213df80d93SArnaldo Carvalho de Melo 		if (opt != np->opt)
6223df80d93SArnaldo Carvalho de Melo 			sock_kfree_s(sk, opt, opt->tot_len);
6233df80d93SArnaldo Carvalho de Melo 	}
6243df80d93SArnaldo Carvalho de Melo 
625d83d8461SArnaldo Carvalho de Melo 	inet_csk(newsk)->icsk_ext_hdr_len = 0;
62645329e71SArnaldo Carvalho de Melo 	if (newnp->opt != NULL)
627d83d8461SArnaldo Carvalho de Melo 		inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
628d83d8461SArnaldo Carvalho de Melo 						     newnp->opt->opt_flen);
6293df80d93SArnaldo Carvalho de Melo 
6303df80d93SArnaldo Carvalho de Melo 	dccp_sync_mss(newsk, dst_mtu(dst));
6313df80d93SArnaldo Carvalho de Melo 
632c720c7e8SEric Dumazet 	newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6;
633c720c7e8SEric Dumazet 	newinet->inet_rcv_saddr = LOOPBACK4_IPV6;
6343df80d93SArnaldo Carvalho de Melo 
635*093d2823SBalazs Scheidler 	if (__inet_inherit_port(sk, newsk) < 0) {
636*093d2823SBalazs Scheidler 		sock_put(newsk);
637*093d2823SBalazs Scheidler 		goto out;
638*093d2823SBalazs Scheidler 	}
6399327f705SEric Dumazet 	__inet6_hash(newsk, NULL);
6403df80d93SArnaldo Carvalho de Melo 
6413df80d93SArnaldo Carvalho de Melo 	return newsk;
6423df80d93SArnaldo Carvalho de Melo 
6433df80d93SArnaldo Carvalho de Melo out_overflow:
644de0744afSPavel Emelyanov 	NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
645*093d2823SBalazs Scheidler out_nonewsk:
646*093d2823SBalazs Scheidler 	dst_release(dst);
6473df80d93SArnaldo Carvalho de Melo out:
648de0744afSPavel Emelyanov 	NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
64945329e71SArnaldo Carvalho de Melo 	if (opt != NULL && opt != np->opt)
6503df80d93SArnaldo Carvalho de Melo 		sock_kfree_s(sk, opt, opt->tot_len);
6513df80d93SArnaldo Carvalho de Melo 	return NULL;
6523df80d93SArnaldo Carvalho de Melo }
6533df80d93SArnaldo Carvalho de Melo 
6543df80d93SArnaldo Carvalho de Melo /* The socket must have it's spinlock held when we get
6553df80d93SArnaldo Carvalho de Melo  * here.
6563df80d93SArnaldo Carvalho de Melo  *
6573df80d93SArnaldo Carvalho de Melo  * We have a potential double-lock case here, so even when
6583df80d93SArnaldo Carvalho de Melo  * doing backlog processing we use the BH locking scheme.
6593df80d93SArnaldo Carvalho de Melo  * This is because we cannot sleep with the original spinlock
6603df80d93SArnaldo Carvalho de Melo  * held.
6613df80d93SArnaldo Carvalho de Melo  */
6623df80d93SArnaldo Carvalho de Melo static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
6633df80d93SArnaldo Carvalho de Melo {
6643df80d93SArnaldo Carvalho de Melo 	struct ipv6_pinfo *np = inet6_sk(sk);
6653df80d93SArnaldo Carvalho de Melo 	struct sk_buff *opt_skb = NULL;
6663df80d93SArnaldo Carvalho de Melo 
6673df80d93SArnaldo Carvalho de Melo 	/* Imagine: socket is IPv6. IPv4 packet arrives,
6683df80d93SArnaldo Carvalho de Melo 	   goes to IPv4 receive handler and backlogged.
6693df80d93SArnaldo Carvalho de Melo 	   From backlog it always goes here. Kerboom...
6703df80d93SArnaldo Carvalho de Melo 	   Fortunately, dccp_rcv_established and rcv_established
6713df80d93SArnaldo Carvalho de Melo 	   handle them correctly, but it is not case with
6723df80d93SArnaldo Carvalho de Melo 	   dccp_v6_hnd_req and dccp_v6_ctl_send_reset().   --ANK
6733df80d93SArnaldo Carvalho de Melo 	 */
6743df80d93SArnaldo Carvalho de Melo 
6753df80d93SArnaldo Carvalho de Melo 	if (skb->protocol == htons(ETH_P_IP))
6763df80d93SArnaldo Carvalho de Melo 		return dccp_v4_do_rcv(sk, skb);
6773df80d93SArnaldo Carvalho de Melo 
678fda9ef5dSDmitry Mishin 	if (sk_filter(sk, skb))
6793df80d93SArnaldo Carvalho de Melo 		goto discard;
6803df80d93SArnaldo Carvalho de Melo 
6813df80d93SArnaldo Carvalho de Melo 	/*
68245329e71SArnaldo Carvalho de Melo 	 * socket locking is here for SMP purposes as backlog rcv is currently
68345329e71SArnaldo Carvalho de Melo 	 * called with bh processing disabled.
6843df80d93SArnaldo Carvalho de Melo 	 */
6853df80d93SArnaldo Carvalho de Melo 
6863df80d93SArnaldo Carvalho de Melo 	/* Do Stevens' IPV6_PKTOPTIONS.
6873df80d93SArnaldo Carvalho de Melo 
6883df80d93SArnaldo Carvalho de Melo 	   Yes, guys, it is the only place in our code, where we
6893df80d93SArnaldo Carvalho de Melo 	   may make it not affecting IPv4.
6903df80d93SArnaldo Carvalho de Melo 	   The rest of code is protocol independent,
6913df80d93SArnaldo Carvalho de Melo 	   and I do not like idea to uglify IPv4.
6923df80d93SArnaldo Carvalho de Melo 
6933df80d93SArnaldo Carvalho de Melo 	   Actually, all the idea behind IPV6_PKTOPTIONS
6943df80d93SArnaldo Carvalho de Melo 	   looks not very well thought. For now we latch
6953df80d93SArnaldo Carvalho de Melo 	   options, received in the last packet, enqueued
6963df80d93SArnaldo Carvalho de Melo 	   by tcp. Feel free to propose better solution.
6973df80d93SArnaldo Carvalho de Melo 					       --ANK (980728)
6983df80d93SArnaldo Carvalho de Melo 	 */
6993df80d93SArnaldo Carvalho de Melo 	if (np->rxopt.all)
70089e7e577SGerrit Renker 	/*
70189e7e577SGerrit Renker 	 * FIXME: Add handling of IPV6_PKTOPTIONS skb. See the comments below
70289e7e577SGerrit Renker 	 *        (wrt ipv6_pktopions) and net/ipv6/tcp_ipv6.c for an example.
70389e7e577SGerrit Renker 	 */
7043df80d93SArnaldo Carvalho de Melo 		opt_skb = skb_clone(skb, GFP_ATOMIC);
7053df80d93SArnaldo Carvalho de Melo 
7063df80d93SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_OPEN) { /* Fast path */
7073df80d93SArnaldo Carvalho de Melo 		if (dccp_rcv_established(sk, skb, dccp_hdr(skb), skb->len))
7083df80d93SArnaldo Carvalho de Melo 			goto reset;
709fd169f15SDavid S. Miller 		if (opt_skb) {
71089e7e577SGerrit Renker 			/* XXX This is where we would goto ipv6_pktoptions. */
711fd169f15SDavid S. Miller 			__kfree_skb(opt_skb);
712fd169f15SDavid S. Miller 		}
7133df80d93SArnaldo Carvalho de Melo 		return 0;
7143df80d93SArnaldo Carvalho de Melo 	}
7153df80d93SArnaldo Carvalho de Melo 
716d83ca5acSGerrit Renker 	/*
717d83ca5acSGerrit Renker 	 *  Step 3: Process LISTEN state
718d83ca5acSGerrit Renker 	 *     If S.state == LISTEN,
719d83ca5acSGerrit Renker 	 *	 If P.type == Request or P contains a valid Init Cookie option,
720d83ca5acSGerrit Renker 	 *	      (* Must scan the packet's options to check for Init
721d83ca5acSGerrit Renker 	 *		 Cookies.  Only Init Cookies are processed here,
722d83ca5acSGerrit Renker 	 *		 however; other options are processed in Step 8.  This
723d83ca5acSGerrit Renker 	 *		 scan need only be performed if the endpoint uses Init
724d83ca5acSGerrit Renker 	 *		 Cookies *)
725d83ca5acSGerrit Renker 	 *	      (* Generate a new socket and switch to that socket *)
726d83ca5acSGerrit Renker 	 *	      Set S := new socket for this port pair
727d83ca5acSGerrit Renker 	 *	      S.state = RESPOND
728d83ca5acSGerrit Renker 	 *	      Choose S.ISS (initial seqno) or set from Init Cookies
729d83ca5acSGerrit Renker 	 *	      Initialize S.GAR := S.ISS
730d83ca5acSGerrit Renker 	 *	      Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookies
731d83ca5acSGerrit Renker 	 *	      Continue with S.state == RESPOND
732d83ca5acSGerrit Renker 	 *	      (* A Response packet will be generated in Step 11 *)
733d83ca5acSGerrit Renker 	 *	 Otherwise,
734d83ca5acSGerrit Renker 	 *	      Generate Reset(No Connection) unless P.type == Reset
735d83ca5acSGerrit Renker 	 *	      Drop packet and return
736d83ca5acSGerrit Renker 	 *
737d83ca5acSGerrit Renker 	 * NOTE: the check for the packet types is done in
738d83ca5acSGerrit Renker 	 *	 dccp_rcv_state_process
739d83ca5acSGerrit Renker 	 */
7403df80d93SArnaldo Carvalho de Melo 	if (sk->sk_state == DCCP_LISTEN) {
7413df80d93SArnaldo Carvalho de Melo 		struct sock *nsk = dccp_v6_hnd_req(sk, skb);
7423df80d93SArnaldo Carvalho de Melo 
74345329e71SArnaldo Carvalho de Melo 		if (nsk == NULL)
74445329e71SArnaldo Carvalho de Melo 			goto discard;
7453df80d93SArnaldo Carvalho de Melo 		/*
7463df80d93SArnaldo Carvalho de Melo 		 * Queue it on the new socket if the new socket is active,
7473df80d93SArnaldo Carvalho de Melo 		 * otherwise we just shortcircuit this and continue with
7483df80d93SArnaldo Carvalho de Melo 		 * the new socket..
7493df80d93SArnaldo Carvalho de Melo 		 */
7503df80d93SArnaldo Carvalho de Melo 		if (nsk != sk) {
7513df80d93SArnaldo Carvalho de Melo 			if (dccp_child_process(sk, nsk, skb))
7523df80d93SArnaldo Carvalho de Melo 				goto reset;
75345329e71SArnaldo Carvalho de Melo 			if (opt_skb != NULL)
7543df80d93SArnaldo Carvalho de Melo 				__kfree_skb(opt_skb);
7553df80d93SArnaldo Carvalho de Melo 			return 0;
7563df80d93SArnaldo Carvalho de Melo 		}
7573df80d93SArnaldo Carvalho de Melo 	}
7583df80d93SArnaldo Carvalho de Melo 
7593df80d93SArnaldo Carvalho de Melo 	if (dccp_rcv_state_process(sk, skb, dccp_hdr(skb), skb->len))
7603df80d93SArnaldo Carvalho de Melo 		goto reset;
761fd169f15SDavid S. Miller 	if (opt_skb) {
76289e7e577SGerrit Renker 		/* XXX This is where we would goto ipv6_pktoptions. */
763fd169f15SDavid S. Miller 		__kfree_skb(opt_skb);
764fd169f15SDavid S. Miller 	}
7653df80d93SArnaldo Carvalho de Melo 	return 0;
7663df80d93SArnaldo Carvalho de Melo 
7673df80d93SArnaldo Carvalho de Melo reset:
768cfb6eeb4SYOSHIFUJI Hideaki 	dccp_v6_ctl_send_reset(sk, skb);
7693df80d93SArnaldo Carvalho de Melo discard:
77045329e71SArnaldo Carvalho de Melo 	if (opt_skb != NULL)
7713df80d93SArnaldo Carvalho de Melo 		__kfree_skb(opt_skb);
7723df80d93SArnaldo Carvalho de Melo 	kfree_skb(skb);
7733df80d93SArnaldo Carvalho de Melo 	return 0;
7743df80d93SArnaldo Carvalho de Melo }
7753df80d93SArnaldo Carvalho de Melo 
776e5bbef20SHerbert Xu static int dccp_v6_rcv(struct sk_buff *skb)
7773df80d93SArnaldo Carvalho de Melo {
7783df80d93SArnaldo Carvalho de Melo 	const struct dccp_hdr *dh;
7793df80d93SArnaldo Carvalho de Melo 	struct sock *sk;
7806f4e5fffSGerrit Renker 	int min_cov;
7813df80d93SArnaldo Carvalho de Melo 
7826f4e5fffSGerrit Renker 	/* Step 1: Check header basics */
7833df80d93SArnaldo Carvalho de Melo 
7843df80d93SArnaldo Carvalho de Melo 	if (dccp_invalid_packet(skb))
7853df80d93SArnaldo Carvalho de Melo 		goto discard_it;
7863df80d93SArnaldo Carvalho de Melo 
7876f4e5fffSGerrit Renker 	/* Step 1: If header checksum is incorrect, drop packet and return. */
7880660e03fSArnaldo Carvalho de Melo 	if (dccp_v6_csum_finish(skb, &ipv6_hdr(skb)->saddr,
7890660e03fSArnaldo Carvalho de Melo 				     &ipv6_hdr(skb)->daddr)) {
79059348b19SGerrit Renker 		DCCP_WARN("dropped packet with invalid checksum\n");
7916f4e5fffSGerrit Renker 		goto discard_it;
7926f4e5fffSGerrit Renker 	}
7936f4e5fffSGerrit Renker 
7943df80d93SArnaldo Carvalho de Melo 	dh = dccp_hdr(skb);
7953df80d93SArnaldo Carvalho de Melo 
796fde20105SGerrit Renker 	DCCP_SKB_CB(skb)->dccpd_seq  = dccp_hdr_seq(dh);
7973df80d93SArnaldo Carvalho de Melo 	DCCP_SKB_CB(skb)->dccpd_type = dh->dccph_type;
7983df80d93SArnaldo Carvalho de Melo 
7993df80d93SArnaldo Carvalho de Melo 	if (dccp_packet_without_ack(skb))
8003df80d93SArnaldo Carvalho de Melo 		DCCP_SKB_CB(skb)->dccpd_ack_seq = DCCP_PKT_WITHOUT_ACK_SEQ;
8013df80d93SArnaldo Carvalho de Melo 	else
8023df80d93SArnaldo Carvalho de Melo 		DCCP_SKB_CB(skb)->dccpd_ack_seq = dccp_hdr_ack_seq(skb);
8033df80d93SArnaldo Carvalho de Melo 
8043df80d93SArnaldo Carvalho de Melo 	/* Step 2:
8053df80d93SArnaldo Carvalho de Melo 	 *	Look up flow ID in table and get corresponding socket */
8069a1f27c4SArnaldo Carvalho de Melo 	sk = __inet6_lookup_skb(&dccp_hashinfo, skb,
8079a1f27c4SArnaldo Carvalho de Melo 			        dh->dccph_sport, dh->dccph_dport);
8083df80d93SArnaldo Carvalho de Melo 	/*
8093df80d93SArnaldo Carvalho de Melo 	 * Step 2:
8103df80d93SArnaldo Carvalho de Melo 	 *	If no socket ...
8113df80d93SArnaldo Carvalho de Melo 	 */
812d23c7107SGerrit Renker 	if (sk == NULL) {
813d23c7107SGerrit Renker 		dccp_pr_debug("failed to look up flow ID in table and "
814d23c7107SGerrit Renker 			      "get corresponding socket\n");
8153df80d93SArnaldo Carvalho de Melo 		goto no_dccp_socket;
816d23c7107SGerrit Renker 	}
8173df80d93SArnaldo Carvalho de Melo 
8183df80d93SArnaldo Carvalho de Melo 	/*
8193df80d93SArnaldo Carvalho de Melo 	 * Step 2:
8203df80d93SArnaldo Carvalho de Melo 	 *	... or S.state == TIMEWAIT,
8213df80d93SArnaldo Carvalho de Melo 	 *		Generate Reset(No Connection) unless P.type == Reset
8223df80d93SArnaldo Carvalho de Melo 	 *		Drop packet and return
8233df80d93SArnaldo Carvalho de Melo 	 */
824d23c7107SGerrit Renker 	if (sk->sk_state == DCCP_TIME_WAIT) {
825d23c7107SGerrit Renker 		dccp_pr_debug("sk->sk_state == DCCP_TIME_WAIT: do_time_wait\n");
826d23c7107SGerrit Renker 		inet_twsk_put(inet_twsk(sk));
827d23c7107SGerrit Renker 		goto no_dccp_socket;
828d23c7107SGerrit Renker 	}
8293df80d93SArnaldo Carvalho de Melo 
8306f4e5fffSGerrit Renker 	/*
8316f4e5fffSGerrit Renker 	 * RFC 4340, sec. 9.2.1: Minimum Checksum Coverage
8326f4e5fffSGerrit Renker 	 *	o if MinCsCov = 0, only packets with CsCov = 0 are accepted
8336f4e5fffSGerrit Renker 	 *	o if MinCsCov > 0, also accept packets with CsCov >= MinCsCov
8346f4e5fffSGerrit Renker 	 */
8356f4e5fffSGerrit Renker 	min_cov = dccp_sk(sk)->dccps_pcrlen;
8366f4e5fffSGerrit Renker 	if (dh->dccph_cscov  &&  (min_cov == 0 || dh->dccph_cscov < min_cov))  {
8376f4e5fffSGerrit Renker 		dccp_pr_debug("Packet CsCov %d does not satisfy MinCsCov %d\n",
8386f4e5fffSGerrit Renker 			      dh->dccph_cscov, min_cov);
8396f4e5fffSGerrit Renker 		/* FIXME: send Data Dropped option (see also dccp_v4_rcv) */
8406f4e5fffSGerrit Renker 		goto discard_and_relse;
8416f4e5fffSGerrit Renker 	}
8426f4e5fffSGerrit Renker 
8433df80d93SArnaldo Carvalho de Melo 	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
8443df80d93SArnaldo Carvalho de Melo 		goto discard_and_relse;
8453df80d93SArnaldo Carvalho de Melo 
84658a5a7b9SArnaldo Carvalho de Melo 	return sk_receive_skb(sk, skb, 1) ? -1 : 0;
8473df80d93SArnaldo Carvalho de Melo 
8483df80d93SArnaldo Carvalho de Melo no_dccp_socket:
8493df80d93SArnaldo Carvalho de Melo 	if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
8503df80d93SArnaldo Carvalho de Melo 		goto discard_it;
8513df80d93SArnaldo Carvalho de Melo 	/*
8523df80d93SArnaldo Carvalho de Melo 	 * Step 2:
853d83ca5acSGerrit Renker 	 *	If no socket ...
8543df80d93SArnaldo Carvalho de Melo 	 *		Generate Reset(No Connection) unless P.type == Reset
8553df80d93SArnaldo Carvalho de Melo 	 *		Drop packet and return
8563df80d93SArnaldo Carvalho de Melo 	 */
8573df80d93SArnaldo Carvalho de Melo 	if (dh->dccph_type != DCCP_PKT_RESET) {
8583df80d93SArnaldo Carvalho de Melo 		DCCP_SKB_CB(skb)->dccpd_reset_code =
8593df80d93SArnaldo Carvalho de Melo 					DCCP_RESET_CODE_NO_CONNECTION;
860cfb6eeb4SYOSHIFUJI Hideaki 		dccp_v6_ctl_send_reset(sk, skb);
8613df80d93SArnaldo Carvalho de Melo 	}
862d23c7107SGerrit Renker 
8633df80d93SArnaldo Carvalho de Melo discard_it:
8643df80d93SArnaldo Carvalho de Melo 	kfree_skb(skb);
8653df80d93SArnaldo Carvalho de Melo 	return 0;
8663df80d93SArnaldo Carvalho de Melo 
8673df80d93SArnaldo Carvalho de Melo discard_and_relse:
8683df80d93SArnaldo Carvalho de Melo 	sock_put(sk);
8693df80d93SArnaldo Carvalho de Melo 	goto discard_it;
8703df80d93SArnaldo Carvalho de Melo }
8713df80d93SArnaldo Carvalho de Melo 
87273c9e02cSGerrit Renker static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
87373c9e02cSGerrit Renker 			   int addr_len)
87473c9e02cSGerrit Renker {
87573c9e02cSGerrit Renker 	struct sockaddr_in6 *usin = (struct sockaddr_in6 *)uaddr;
87673c9e02cSGerrit Renker 	struct inet_connection_sock *icsk = inet_csk(sk);
87773c9e02cSGerrit Renker 	struct inet_sock *inet = inet_sk(sk);
87873c9e02cSGerrit Renker 	struct ipv6_pinfo *np = inet6_sk(sk);
87973c9e02cSGerrit Renker 	struct dccp_sock *dp = dccp_sk(sk);
88020c59de2SArnaud Ebalard 	struct in6_addr *saddr = NULL, *final_p, final;
88173c9e02cSGerrit Renker 	struct flowi fl;
88273c9e02cSGerrit Renker 	struct dst_entry *dst;
88373c9e02cSGerrit Renker 	int addr_type;
88473c9e02cSGerrit Renker 	int err;
88573c9e02cSGerrit Renker 
88673c9e02cSGerrit Renker 	dp->dccps_role = DCCP_ROLE_CLIENT;
88773c9e02cSGerrit Renker 
88873c9e02cSGerrit Renker 	if (addr_len < SIN6_LEN_RFC2133)
88973c9e02cSGerrit Renker 		return -EINVAL;
89073c9e02cSGerrit Renker 
89173c9e02cSGerrit Renker 	if (usin->sin6_family != AF_INET6)
89273c9e02cSGerrit Renker 		return -EAFNOSUPPORT;
89373c9e02cSGerrit Renker 
89473c9e02cSGerrit Renker 	memset(&fl, 0, sizeof(fl));
89573c9e02cSGerrit Renker 
89673c9e02cSGerrit Renker 	if (np->sndflow) {
89773c9e02cSGerrit Renker 		fl.fl6_flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK;
89873c9e02cSGerrit Renker 		IP6_ECN_flow_init(fl.fl6_flowlabel);
89973c9e02cSGerrit Renker 		if (fl.fl6_flowlabel & IPV6_FLOWLABEL_MASK) {
90073c9e02cSGerrit Renker 			struct ip6_flowlabel *flowlabel;
90173c9e02cSGerrit Renker 			flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
90273c9e02cSGerrit Renker 			if (flowlabel == NULL)
90373c9e02cSGerrit Renker 				return -EINVAL;
90473c9e02cSGerrit Renker 			ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
90573c9e02cSGerrit Renker 			fl6_sock_release(flowlabel);
90673c9e02cSGerrit Renker 		}
90773c9e02cSGerrit Renker 	}
90873c9e02cSGerrit Renker 	/*
90973c9e02cSGerrit Renker 	 * connect() to INADDR_ANY means loopback (BSD'ism).
91073c9e02cSGerrit Renker 	 */
91173c9e02cSGerrit Renker 	if (ipv6_addr_any(&usin->sin6_addr))
91273c9e02cSGerrit Renker 		usin->sin6_addr.s6_addr[15] = 1;
91373c9e02cSGerrit Renker 
91473c9e02cSGerrit Renker 	addr_type = ipv6_addr_type(&usin->sin6_addr);
91573c9e02cSGerrit Renker 
91673c9e02cSGerrit Renker 	if (addr_type & IPV6_ADDR_MULTICAST)
91773c9e02cSGerrit Renker 		return -ENETUNREACH;
91873c9e02cSGerrit Renker 
91973c9e02cSGerrit Renker 	if (addr_type & IPV6_ADDR_LINKLOCAL) {
92073c9e02cSGerrit Renker 		if (addr_len >= sizeof(struct sockaddr_in6) &&
92173c9e02cSGerrit Renker 		    usin->sin6_scope_id) {
92273c9e02cSGerrit Renker 			/* If interface is set while binding, indices
92373c9e02cSGerrit Renker 			 * must coincide.
92473c9e02cSGerrit Renker 			 */
92573c9e02cSGerrit Renker 			if (sk->sk_bound_dev_if &&
92673c9e02cSGerrit Renker 			    sk->sk_bound_dev_if != usin->sin6_scope_id)
92773c9e02cSGerrit Renker 				return -EINVAL;
92873c9e02cSGerrit Renker 
92973c9e02cSGerrit Renker 			sk->sk_bound_dev_if = usin->sin6_scope_id;
93073c9e02cSGerrit Renker 		}
93173c9e02cSGerrit Renker 
93273c9e02cSGerrit Renker 		/* Connect to link-local address requires an interface */
93373c9e02cSGerrit Renker 		if (!sk->sk_bound_dev_if)
93473c9e02cSGerrit Renker 			return -EINVAL;
93573c9e02cSGerrit Renker 	}
93673c9e02cSGerrit Renker 
93773c9e02cSGerrit Renker 	ipv6_addr_copy(&np->daddr, &usin->sin6_addr);
93873c9e02cSGerrit Renker 	np->flow_label = fl.fl6_flowlabel;
93973c9e02cSGerrit Renker 
94073c9e02cSGerrit Renker 	/*
94173c9e02cSGerrit Renker 	 * DCCP over IPv4
94273c9e02cSGerrit Renker 	 */
94373c9e02cSGerrit Renker 	if (addr_type == IPV6_ADDR_MAPPED) {
94473c9e02cSGerrit Renker 		u32 exthdrlen = icsk->icsk_ext_hdr_len;
94573c9e02cSGerrit Renker 		struct sockaddr_in sin;
94673c9e02cSGerrit Renker 
94773c9e02cSGerrit Renker 		SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
94873c9e02cSGerrit Renker 
94973c9e02cSGerrit Renker 		if (__ipv6_only_sock(sk))
95073c9e02cSGerrit Renker 			return -ENETUNREACH;
95173c9e02cSGerrit Renker 
95273c9e02cSGerrit Renker 		sin.sin_family = AF_INET;
95373c9e02cSGerrit Renker 		sin.sin_port = usin->sin6_port;
95473c9e02cSGerrit Renker 		sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
95573c9e02cSGerrit Renker 
95673c9e02cSGerrit Renker 		icsk->icsk_af_ops = &dccp_ipv6_mapped;
95773c9e02cSGerrit Renker 		sk->sk_backlog_rcv = dccp_v4_do_rcv;
95873c9e02cSGerrit Renker 
95973c9e02cSGerrit Renker 		err = dccp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
96073c9e02cSGerrit Renker 		if (err) {
96173c9e02cSGerrit Renker 			icsk->icsk_ext_hdr_len = exthdrlen;
96273c9e02cSGerrit Renker 			icsk->icsk_af_ops = &dccp_ipv6_af_ops;
96373c9e02cSGerrit Renker 			sk->sk_backlog_rcv = dccp_v6_do_rcv;
96473c9e02cSGerrit Renker 			goto failure;
96573c9e02cSGerrit Renker 		}
966c720c7e8SEric Dumazet 		ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr);
967c720c7e8SEric Dumazet 		ipv6_addr_set_v4mapped(inet->inet_rcv_saddr, &np->rcv_saddr);
96873c9e02cSGerrit Renker 
96973c9e02cSGerrit Renker 		return err;
97073c9e02cSGerrit Renker 	}
97173c9e02cSGerrit Renker 
97273c9e02cSGerrit Renker 	if (!ipv6_addr_any(&np->rcv_saddr))
97373c9e02cSGerrit Renker 		saddr = &np->rcv_saddr;
97473c9e02cSGerrit Renker 
97573c9e02cSGerrit Renker 	fl.proto = IPPROTO_DCCP;
97673c9e02cSGerrit Renker 	ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
97773c9e02cSGerrit Renker 	ipv6_addr_copy(&fl.fl6_src, saddr ? saddr : &np->saddr);
97873c9e02cSGerrit Renker 	fl.oif = sk->sk_bound_dev_if;
97973c9e02cSGerrit Renker 	fl.fl_ip_dport = usin->sin6_port;
980c720c7e8SEric Dumazet 	fl.fl_ip_sport = inet->inet_sport;
98173c9e02cSGerrit Renker 	security_sk_classify_flow(sk, &fl);
98273c9e02cSGerrit Renker 
98320c59de2SArnaud Ebalard 	final_p = fl6_update_dst(&fl, np->opt, &final);
98473c9e02cSGerrit Renker 
98573c9e02cSGerrit Renker 	err = ip6_dst_lookup(sk, &dst, &fl);
98673c9e02cSGerrit Renker 	if (err)
98773c9e02cSGerrit Renker 		goto failure;
98873c9e02cSGerrit Renker 
98973c9e02cSGerrit Renker 	if (final_p)
99073c9e02cSGerrit Renker 		ipv6_addr_copy(&fl.fl6_dst, final_p);
99173c9e02cSGerrit Renker 
99252479b62SAlexey Dobriyan 	err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT);
99314e50e57SDavid S. Miller 	if (err < 0) {
99414e50e57SDavid S. Miller 		if (err == -EREMOTE)
99514e50e57SDavid S. Miller 			err = ip6_dst_blackhole(sk, &dst, &fl);
99673c9e02cSGerrit Renker 		if (err < 0)
99773c9e02cSGerrit Renker 			goto failure;
99814e50e57SDavid S. Miller 	}
99973c9e02cSGerrit Renker 
100073c9e02cSGerrit Renker 	if (saddr == NULL) {
100173c9e02cSGerrit Renker 		saddr = &fl.fl6_src;
100273c9e02cSGerrit Renker 		ipv6_addr_copy(&np->rcv_saddr, saddr);
100373c9e02cSGerrit Renker 	}
100473c9e02cSGerrit Renker 
100573c9e02cSGerrit Renker 	/* set the source address */
100673c9e02cSGerrit Renker 	ipv6_addr_copy(&np->saddr, saddr);
1007c720c7e8SEric Dumazet 	inet->inet_rcv_saddr = LOOPBACK4_IPV6;
100873c9e02cSGerrit Renker 
100973c9e02cSGerrit Renker 	__ip6_dst_store(sk, dst, NULL, NULL);
101073c9e02cSGerrit Renker 
101173c9e02cSGerrit Renker 	icsk->icsk_ext_hdr_len = 0;
101273c9e02cSGerrit Renker 	if (np->opt != NULL)
101373c9e02cSGerrit Renker 		icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
101473c9e02cSGerrit Renker 					  np->opt->opt_nflen);
101573c9e02cSGerrit Renker 
1016c720c7e8SEric Dumazet 	inet->inet_dport = usin->sin6_port;
101773c9e02cSGerrit Renker 
101873c9e02cSGerrit Renker 	dccp_set_state(sk, DCCP_REQUESTING);
101973c9e02cSGerrit Renker 	err = inet6_hash_connect(&dccp_death_row, sk);
102073c9e02cSGerrit Renker 	if (err)
102173c9e02cSGerrit Renker 		goto late_failure;
1022d7f7365fSGerrit Renker 
1023d7f7365fSGerrit Renker 	dp->dccps_iss = secure_dccpv6_sequence_number(np->saddr.s6_addr32,
102473c9e02cSGerrit Renker 						      np->daddr.s6_addr32,
1025c720c7e8SEric Dumazet 						      inet->inet_sport,
1026c720c7e8SEric Dumazet 						      inet->inet_dport);
102773c9e02cSGerrit Renker 	err = dccp_connect(sk);
102873c9e02cSGerrit Renker 	if (err)
102973c9e02cSGerrit Renker 		goto late_failure;
103073c9e02cSGerrit Renker 
103173c9e02cSGerrit Renker 	return 0;
103273c9e02cSGerrit Renker 
103373c9e02cSGerrit Renker late_failure:
103473c9e02cSGerrit Renker 	dccp_set_state(sk, DCCP_CLOSED);
103573c9e02cSGerrit Renker 	__sk_dst_reset(sk);
103673c9e02cSGerrit Renker failure:
1037c720c7e8SEric Dumazet 	inet->inet_dport = 0;
103873c9e02cSGerrit Renker 	sk->sk_route_caps = 0;
103973c9e02cSGerrit Renker 	return err;
104073c9e02cSGerrit Renker }
104173c9e02cSGerrit Renker 
10423b401a81SStephen Hemminger static const struct inet_connection_sock_af_ops dccp_ipv6_af_ops = {
10433df80d93SArnaldo Carvalho de Melo 	.queue_xmit	   = inet6_csk_xmit,
10443df80d93SArnaldo Carvalho de Melo 	.send_check	   = dccp_v6_send_check,
10453df80d93SArnaldo Carvalho de Melo 	.rebuild_header	   = inet6_sk_rebuild_header,
10463df80d93SArnaldo Carvalho de Melo 	.conn_request	   = dccp_v6_conn_request,
10473df80d93SArnaldo Carvalho de Melo 	.syn_recv_sock	   = dccp_v6_request_recv_sock,
10483df80d93SArnaldo Carvalho de Melo 	.net_header_len	   = sizeof(struct ipv6hdr),
10493df80d93SArnaldo Carvalho de Melo 	.setsockopt	   = ipv6_setsockopt,
10503df80d93SArnaldo Carvalho de Melo 	.getsockopt	   = ipv6_getsockopt,
1051543d9cfeSArnaldo Carvalho de Melo 	.addr2sockaddr	   = inet6_csk_addr2sockaddr,
1052543d9cfeSArnaldo Carvalho de Melo 	.sockaddr_len	   = sizeof(struct sockaddr_in6),
1053ab1e0a13SArnaldo Carvalho de Melo 	.bind_conflict	   = inet6_csk_bind_conflict,
10543fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT
10553fdadf7dSDmitry Mishin 	.compat_setsockopt = compat_ipv6_setsockopt,
10563fdadf7dSDmitry Mishin 	.compat_getsockopt = compat_ipv6_getsockopt,
10573fdadf7dSDmitry Mishin #endif
10583df80d93SArnaldo Carvalho de Melo };
10593df80d93SArnaldo Carvalho de Melo 
10603df80d93SArnaldo Carvalho de Melo /*
10613df80d93SArnaldo Carvalho de Melo  *	DCCP over IPv4 via INET6 API
10623df80d93SArnaldo Carvalho de Melo  */
10633b401a81SStephen Hemminger static const struct inet_connection_sock_af_ops dccp_ipv6_mapped = {
10643df80d93SArnaldo Carvalho de Melo 	.queue_xmit	   = ip_queue_xmit,
10653df80d93SArnaldo Carvalho de Melo 	.send_check	   = dccp_v4_send_check,
10663df80d93SArnaldo Carvalho de Melo 	.rebuild_header	   = inet_sk_rebuild_header,
10673df80d93SArnaldo Carvalho de Melo 	.conn_request	   = dccp_v6_conn_request,
10683df80d93SArnaldo Carvalho de Melo 	.syn_recv_sock	   = dccp_v6_request_recv_sock,
10693df80d93SArnaldo Carvalho de Melo 	.net_header_len	   = sizeof(struct iphdr),
10703df80d93SArnaldo Carvalho de Melo 	.setsockopt	   = ipv6_setsockopt,
10713df80d93SArnaldo Carvalho de Melo 	.getsockopt	   = ipv6_getsockopt,
1072543d9cfeSArnaldo Carvalho de Melo 	.addr2sockaddr	   = inet6_csk_addr2sockaddr,
1073543d9cfeSArnaldo Carvalho de Melo 	.sockaddr_len	   = sizeof(struct sockaddr_in6),
10743fdadf7dSDmitry Mishin #ifdef CONFIG_COMPAT
10753fdadf7dSDmitry Mishin 	.compat_setsockopt = compat_ipv6_setsockopt,
10763fdadf7dSDmitry Mishin 	.compat_getsockopt = compat_ipv6_getsockopt,
10773fdadf7dSDmitry Mishin #endif
10783df80d93SArnaldo Carvalho de Melo };
10793df80d93SArnaldo Carvalho de Melo 
10803df80d93SArnaldo Carvalho de Melo /* NOTE: A lot of things set to zero explicitly by call to
10813df80d93SArnaldo Carvalho de Melo  *       sk_alloc() so need not be done here.
10823df80d93SArnaldo Carvalho de Melo  */
10833df80d93SArnaldo Carvalho de Melo static int dccp_v6_init_sock(struct sock *sk)
10843df80d93SArnaldo Carvalho de Melo {
108572478873SArnaldo Carvalho de Melo 	static __u8 dccp_v6_ctl_sock_initialized;
108672478873SArnaldo Carvalho de Melo 	int err = dccp_init_sock(sk, dccp_v6_ctl_sock_initialized);
10873df80d93SArnaldo Carvalho de Melo 
108872478873SArnaldo Carvalho de Melo 	if (err == 0) {
108972478873SArnaldo Carvalho de Melo 		if (unlikely(!dccp_v6_ctl_sock_initialized))
109072478873SArnaldo Carvalho de Melo 			dccp_v6_ctl_sock_initialized = 1;
10913df80d93SArnaldo Carvalho de Melo 		inet_csk(sk)->icsk_af_ops = &dccp_ipv6_af_ops;
109272478873SArnaldo Carvalho de Melo 	}
10933df80d93SArnaldo Carvalho de Melo 
10943df80d93SArnaldo Carvalho de Melo 	return err;
10953df80d93SArnaldo Carvalho de Melo }
10963df80d93SArnaldo Carvalho de Melo 
10977d06b2e0SBrian Haley static void dccp_v6_destroy_sock(struct sock *sk)
10983df80d93SArnaldo Carvalho de Melo {
10993e0fadc5SArnaldo Carvalho de Melo 	dccp_destroy_sock(sk);
11007d06b2e0SBrian Haley 	inet6_destroy_sock(sk);
11013df80d93SArnaldo Carvalho de Melo }
11023df80d93SArnaldo Carvalho de Melo 
110373c9e02cSGerrit Renker static struct timewait_sock_ops dccp6_timewait_sock_ops = {
110473c9e02cSGerrit Renker 	.twsk_obj_size	= sizeof(struct dccp6_timewait_sock),
110573c9e02cSGerrit Renker };
110673c9e02cSGerrit Renker 
11073df80d93SArnaldo Carvalho de Melo static struct proto dccp_v6_prot = {
11083df80d93SArnaldo Carvalho de Melo 	.name		   = "DCCPv6",
11093df80d93SArnaldo Carvalho de Melo 	.owner		   = THIS_MODULE,
11103df80d93SArnaldo Carvalho de Melo 	.close		   = dccp_close,
11113df80d93SArnaldo Carvalho de Melo 	.connect	   = dccp_v6_connect,
11123df80d93SArnaldo Carvalho de Melo 	.disconnect	   = dccp_disconnect,
11133df80d93SArnaldo Carvalho de Melo 	.ioctl		   = dccp_ioctl,
11143df80d93SArnaldo Carvalho de Melo 	.init		   = dccp_v6_init_sock,
11153df80d93SArnaldo Carvalho de Melo 	.setsockopt	   = dccp_setsockopt,
11163df80d93SArnaldo Carvalho de Melo 	.getsockopt	   = dccp_getsockopt,
11173df80d93SArnaldo Carvalho de Melo 	.sendmsg	   = dccp_sendmsg,
11183df80d93SArnaldo Carvalho de Melo 	.recvmsg	   = dccp_recvmsg,
11193df80d93SArnaldo Carvalho de Melo 	.backlog_rcv	   = dccp_v6_do_rcv,
11203df80d93SArnaldo Carvalho de Melo 	.hash		   = dccp_v6_hash,
1121ab1e0a13SArnaldo Carvalho de Melo 	.unhash		   = inet_unhash,
11223df80d93SArnaldo Carvalho de Melo 	.accept		   = inet_csk_accept,
1123ab1e0a13SArnaldo Carvalho de Melo 	.get_port	   = inet_csk_get_port,
11243df80d93SArnaldo Carvalho de Melo 	.shutdown	   = dccp_shutdown,
11253df80d93SArnaldo Carvalho de Melo 	.destroy	   = dccp_v6_destroy_sock,
11263df80d93SArnaldo Carvalho de Melo 	.orphan_count	   = &dccp_orphan_count,
11273df80d93SArnaldo Carvalho de Melo 	.max_header	   = MAX_DCCP_HEADER,
11283df80d93SArnaldo Carvalho de Melo 	.obj_size	   = sizeof(struct dccp6_sock),
11293ab5aee7SEric Dumazet 	.slab_flags	   = SLAB_DESTROY_BY_RCU,
11303df80d93SArnaldo Carvalho de Melo 	.rsk_prot	   = &dccp6_request_sock_ops,
11316d6ee43eSArnaldo Carvalho de Melo 	.twsk_prot	   = &dccp6_timewait_sock_ops,
113239d8cda7SPavel Emelyanov 	.h.hashinfo	   = &dccp_hashinfo,
1133543d9cfeSArnaldo Carvalho de Melo #ifdef CONFIG_COMPAT
1134543d9cfeSArnaldo Carvalho de Melo 	.compat_setsockopt = compat_dccp_setsockopt,
1135543d9cfeSArnaldo Carvalho de Melo 	.compat_getsockopt = compat_dccp_getsockopt,
1136543d9cfeSArnaldo Carvalho de Melo #endif
11373df80d93SArnaldo Carvalho de Melo };
11383df80d93SArnaldo Carvalho de Melo 
113941135cc8SAlexey Dobriyan static const struct inet6_protocol dccp_v6_protocol = {
11403df80d93SArnaldo Carvalho de Melo 	.handler	= dccp_v6_rcv,
11413df80d93SArnaldo Carvalho de Melo 	.err_handler	= dccp_v6_err,
11423df80d93SArnaldo Carvalho de Melo 	.flags		= INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL,
11433df80d93SArnaldo Carvalho de Melo };
11443df80d93SArnaldo Carvalho de Melo 
11455708e868SAlexey Dobriyan static const struct proto_ops inet6_dccp_ops = {
11463df80d93SArnaldo Carvalho de Melo 	.family		   = PF_INET6,
11473df80d93SArnaldo Carvalho de Melo 	.owner		   = THIS_MODULE,
11483df80d93SArnaldo Carvalho de Melo 	.release	   = inet6_release,
11493df80d93SArnaldo Carvalho de Melo 	.bind		   = inet6_bind,
11503df80d93SArnaldo Carvalho de Melo 	.connect	   = inet_stream_connect,
11513df80d93SArnaldo Carvalho de Melo 	.socketpair	   = sock_no_socketpair,
11523df80d93SArnaldo Carvalho de Melo 	.accept		   = inet_accept,
11533df80d93SArnaldo Carvalho de Melo 	.getname	   = inet6_getname,
11543df80d93SArnaldo Carvalho de Melo 	.poll		   = dccp_poll,
11553df80d93SArnaldo Carvalho de Melo 	.ioctl		   = inet6_ioctl,
11563df80d93SArnaldo Carvalho de Melo 	.listen		   = inet_dccp_listen,
11573df80d93SArnaldo Carvalho de Melo 	.shutdown	   = inet_shutdown,
11583df80d93SArnaldo Carvalho de Melo 	.setsockopt	   = sock_common_setsockopt,
11593df80d93SArnaldo Carvalho de Melo 	.getsockopt	   = sock_common_getsockopt,
11603df80d93SArnaldo Carvalho de Melo 	.sendmsg	   = inet_sendmsg,
11613df80d93SArnaldo Carvalho de Melo 	.recvmsg	   = sock_common_recvmsg,
11623df80d93SArnaldo Carvalho de Melo 	.mmap		   = sock_no_mmap,
11633df80d93SArnaldo Carvalho de Melo 	.sendpage	   = sock_no_sendpage,
1164543d9cfeSArnaldo Carvalho de Melo #ifdef CONFIG_COMPAT
1165543d9cfeSArnaldo Carvalho de Melo 	.compat_setsockopt = compat_sock_common_setsockopt,
1166543d9cfeSArnaldo Carvalho de Melo 	.compat_getsockopt = compat_sock_common_getsockopt,
1167543d9cfeSArnaldo Carvalho de Melo #endif
11683df80d93SArnaldo Carvalho de Melo };
11693df80d93SArnaldo Carvalho de Melo 
11703df80d93SArnaldo Carvalho de Melo static struct inet_protosw dccp_v6_protosw = {
11713df80d93SArnaldo Carvalho de Melo 	.type		= SOCK_DCCP,
11723df80d93SArnaldo Carvalho de Melo 	.protocol	= IPPROTO_DCCP,
11733df80d93SArnaldo Carvalho de Melo 	.prot		= &dccp_v6_prot,
11743df80d93SArnaldo Carvalho de Melo 	.ops		= &inet6_dccp_ops,
1175d83d8461SArnaldo Carvalho de Melo 	.flags		= INET_PROTOSW_ICSK,
11763df80d93SArnaldo Carvalho de Melo };
11773df80d93SArnaldo Carvalho de Melo 
11782c8c1e72SAlexey Dobriyan static int __net_init dccp_v6_init_net(struct net *net)
11798231bd27SPavel Emelyanov {
1180d14a0ebdSGerrit Renker 	if (dccp_hashinfo.bhash == NULL)
1181d14a0ebdSGerrit Renker 		return -ESOCKTNOSUPPORT;
1182334527d3SPavel Emelyanov 
1183d14a0ebdSGerrit Renker 	return inet_ctl_sock_create(&net->dccp.v6_ctl_sk, PF_INET6,
1184334527d3SPavel Emelyanov 				    SOCK_DCCP, IPPROTO_DCCP, net);
11858231bd27SPavel Emelyanov }
11868231bd27SPavel Emelyanov 
11872c8c1e72SAlexey Dobriyan static void __net_exit dccp_v6_exit_net(struct net *net)
11888231bd27SPavel Emelyanov {
1189334527d3SPavel Emelyanov 	inet_ctl_sock_destroy(net->dccp.v6_ctl_sk);
11908231bd27SPavel Emelyanov }
11918231bd27SPavel Emelyanov 
11928231bd27SPavel Emelyanov static struct pernet_operations dccp_v6_ops = {
11938231bd27SPavel Emelyanov 	.init   = dccp_v6_init_net,
11948231bd27SPavel Emelyanov 	.exit   = dccp_v6_exit_net,
11958231bd27SPavel Emelyanov };
11968231bd27SPavel Emelyanov 
11973df80d93SArnaldo Carvalho de Melo static int __init dccp_v6_init(void)
11983df80d93SArnaldo Carvalho de Melo {
11993df80d93SArnaldo Carvalho de Melo 	int err = proto_register(&dccp_v6_prot, 1);
12003df80d93SArnaldo Carvalho de Melo 
12013df80d93SArnaldo Carvalho de Melo 	if (err != 0)
12023df80d93SArnaldo Carvalho de Melo 		goto out;
12033df80d93SArnaldo Carvalho de Melo 
12043df80d93SArnaldo Carvalho de Melo 	err = inet6_add_protocol(&dccp_v6_protocol, IPPROTO_DCCP);
12053df80d93SArnaldo Carvalho de Melo 	if (err != 0)
12063df80d93SArnaldo Carvalho de Melo 		goto out_unregister_proto;
12073df80d93SArnaldo Carvalho de Melo 
12083df80d93SArnaldo Carvalho de Melo 	inet6_register_protosw(&dccp_v6_protosw);
120972478873SArnaldo Carvalho de Melo 
12108231bd27SPavel Emelyanov 	err = register_pernet_subsys(&dccp_v6_ops);
12118231bd27SPavel Emelyanov 	if (err != 0)
12128231bd27SPavel Emelyanov 		goto out_destroy_ctl_sock;
12133df80d93SArnaldo Carvalho de Melo out:
12143df80d93SArnaldo Carvalho de Melo 	return err;
12158231bd27SPavel Emelyanov 
12168231bd27SPavel Emelyanov out_destroy_ctl_sock:
121772478873SArnaldo Carvalho de Melo 	inet6_del_protocol(&dccp_v6_protocol, IPPROTO_DCCP);
121872478873SArnaldo Carvalho de Melo 	inet6_unregister_protosw(&dccp_v6_protosw);
12193df80d93SArnaldo Carvalho de Melo out_unregister_proto:
12203df80d93SArnaldo Carvalho de Melo 	proto_unregister(&dccp_v6_prot);
12213df80d93SArnaldo Carvalho de Melo 	goto out;
12223df80d93SArnaldo Carvalho de Melo }
12233df80d93SArnaldo Carvalho de Melo 
12243df80d93SArnaldo Carvalho de Melo static void __exit dccp_v6_exit(void)
12253df80d93SArnaldo Carvalho de Melo {
12268231bd27SPavel Emelyanov 	unregister_pernet_subsys(&dccp_v6_ops);
12273df80d93SArnaldo Carvalho de Melo 	inet6_del_protocol(&dccp_v6_protocol, IPPROTO_DCCP);
12283df80d93SArnaldo Carvalho de Melo 	inet6_unregister_protosw(&dccp_v6_protosw);
12293df80d93SArnaldo Carvalho de Melo 	proto_unregister(&dccp_v6_prot);
12303df80d93SArnaldo Carvalho de Melo }
12313df80d93SArnaldo Carvalho de Melo 
12323df80d93SArnaldo Carvalho de Melo module_init(dccp_v6_init);
12333df80d93SArnaldo Carvalho de Melo module_exit(dccp_v6_exit);
12343df80d93SArnaldo Carvalho de Melo 
12353df80d93SArnaldo Carvalho de Melo /*
12363df80d93SArnaldo Carvalho de Melo  * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33)
12373df80d93SArnaldo Carvalho de Melo  * values directly, Also cover the case where the protocol is not specified,
12383df80d93SArnaldo Carvalho de Melo  * i.e. net-pf-PF_INET6-proto-0-type-SOCK_DCCP
12393df80d93SArnaldo Carvalho de Melo  */
12407131c6c7SJean Delvare MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 33, 6);
12417131c6c7SJean Delvare MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 0, 6);
12423df80d93SArnaldo Carvalho de Melo MODULE_LICENSE("GPL");
12433df80d93SArnaldo Carvalho de Melo MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>");
12443df80d93SArnaldo Carvalho de Melo MODULE_DESCRIPTION("DCCPv6 - Datagram Congestion Controlled Protocol");
1245