xref: /openbmc/linux/net/ipv6/ipv6_sockglue.c (revision 0f158b32)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *	IPv6 BSD socket options interface
41da177e4SLinus Torvalds  *	Linux INET6 implementation
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *	Authors:
71da177e4SLinus Torvalds  *	Pedro Roque		<roque@di.fc.ul.pt>
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *	Based on linux/net/ipv4/ip_sockglue.c
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *	FIXME: Make the setsockopt code POSIX compliant: That is
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  *	o	Truncate getsockopt returns
141da177e4SLinus Torvalds  *	o	Return an optlen of the truncated length if need be
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  *	Changes:
171da177e4SLinus Torvalds  *	David L Stevens <dlstevens@us.ibm.com>:
181da177e4SLinus Torvalds  *		- added multicast source filtering API for MLDv2
191da177e4SLinus Torvalds  */
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds #include <linux/module.h>
224fc268d2SRandy Dunlap #include <linux/capability.h>
231da177e4SLinus Torvalds #include <linux/errno.h>
241da177e4SLinus Torvalds #include <linux/types.h>
251da177e4SLinus Torvalds #include <linux/socket.h>
261da177e4SLinus Torvalds #include <linux/sockios.h>
271da177e4SLinus Torvalds #include <linux/net.h>
281da177e4SLinus Torvalds #include <linux/in6.h>
297bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h>
301da177e4SLinus Torvalds #include <linux/netdevice.h>
311da177e4SLinus Torvalds #include <linux/if_arp.h>
321da177e4SLinus Torvalds #include <linux/init.h>
331da177e4SLinus Torvalds #include <linux/sysctl.h>
341da177e4SLinus Torvalds #include <linux/netfilter.h>
355a0e3ad6STejun Heo #include <linux/slab.h>
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds #include <net/sock.h>
381da177e4SLinus Torvalds #include <net/snmp.h>
391da177e4SLinus Torvalds #include <net/ipv6.h>
401da177e4SLinus Torvalds #include <net/ndisc.h>
411da177e4SLinus Torvalds #include <net/protocol.h>
421da177e4SLinus Torvalds #include <net/transp_v6.h>
431da177e4SLinus Torvalds #include <net/ip6_route.h>
441da177e4SLinus Torvalds #include <net/addrconf.h>
451da177e4SLinus Torvalds #include <net/inet_common.h>
461da177e4SLinus Torvalds #include <net/tcp.h>
471da177e4SLinus Torvalds #include <net/udp.h>
48ba4e58ecSGerrit Renker #include <net/udplite.h>
491da177e4SLinus Torvalds #include <net/xfrm.h>
50dae50295SDavid L Stevens #include <net/compat.h>
51a149e7c7SDavid Lebrun #include <net/seg6.h>
521da177e4SLinus Torvalds 
537c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds struct ip6_ra_chain *ip6_ra_chain;
561da177e4SLinus Torvalds DEFINE_RWLOCK(ip6_ra_lock);
571da177e4SLinus Torvalds 
58790eb673SEric Dumazet DEFINE_STATIC_KEY_FALSE(ip6_min_hopcount);
59790eb673SEric Dumazet 
ip6_ra_control(struct sock * sk,int sel)60725a8ff0SDenis V. Lunev int ip6_ra_control(struct sock *sk, int sel)
611da177e4SLinus Torvalds {
621da177e4SLinus Torvalds 	struct ip6_ra_chain *ra, *new_ra, **rap;
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds 	/* RA packet may be delivered ONLY to IPPROTO_RAW socket */
65c720c7e8SEric Dumazet 	if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num != IPPROTO_RAW)
661717699cSYOSHIFUJI Hideaki 		return -ENOPROTOOPT;
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds 	new_ra = (sel >= 0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
6995baa60aSGen Zhang 	if (sel >= 0 && !new_ra)
7095baa60aSGen Zhang 		return -ENOMEM;
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 	write_lock_bh(&ip6_ra_lock);
731da177e4SLinus Torvalds 	for (rap = &ip6_ra_chain; (ra = *rap) != NULL; rap = &ra->next) {
741da177e4SLinus Torvalds 		if (ra->sk == sk) {
751da177e4SLinus Torvalds 			if (sel >= 0) {
761da177e4SLinus Torvalds 				write_unlock_bh(&ip6_ra_lock);
771da177e4SLinus Torvalds 				kfree(new_ra);
781da177e4SLinus Torvalds 				return -EADDRINUSE;
791da177e4SLinus Torvalds 			}
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds 			*rap = ra->next;
821da177e4SLinus Torvalds 			write_unlock_bh(&ip6_ra_lock);
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds 			sock_put(sk);
851da177e4SLinus Torvalds 			kfree(ra);
861da177e4SLinus Torvalds 			return 0;
871da177e4SLinus Torvalds 		}
881da177e4SLinus Torvalds 	}
8963159f29SIan Morris 	if (!new_ra) {
901da177e4SLinus Torvalds 		write_unlock_bh(&ip6_ra_lock);
911da177e4SLinus Torvalds 		return -ENOBUFS;
921da177e4SLinus Torvalds 	}
931da177e4SLinus Torvalds 	new_ra->sk = sk;
941da177e4SLinus Torvalds 	new_ra->sel = sel;
951da177e4SLinus Torvalds 	new_ra->next = ra;
961da177e4SLinus Torvalds 	*rap = new_ra;
971da177e4SLinus Torvalds 	sock_hold(sk);
981da177e4SLinus Torvalds 	write_unlock_bh(&ip6_ra_lock);
991da177e4SLinus Torvalds 	return 0;
1001da177e4SLinus Torvalds }
1011da177e4SLinus Torvalds 
ipv6_update_options(struct sock * sk,struct ipv6_txoptions * opt)102e7712f1aSYOSHIFUJI Hideaki struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
103e7712f1aSYOSHIFUJI Hideaki 					   struct ipv6_txoptions *opt)
104e7712f1aSYOSHIFUJI Hideaki {
105b1c0356aSEric Dumazet 	if (inet_test_bit(IS_ICSK, sk)) {
106e7712f1aSYOSHIFUJI Hideaki 		if (opt &&
107e7712f1aSYOSHIFUJI Hideaki 		    !((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) &&
108c720c7e8SEric Dumazet 		    inet_sk(sk)->inet_daddr != LOOPBACK4_IPV6) {
109e7712f1aSYOSHIFUJI Hideaki 			struct inet_connection_sock *icsk = inet_csk(sk);
110e7712f1aSYOSHIFUJI Hideaki 			icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen;
111e7712f1aSYOSHIFUJI Hideaki 			icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
112e7712f1aSYOSHIFUJI Hideaki 		}
113e7712f1aSYOSHIFUJI Hideaki 	}
11445f6fad8SEric Dumazet 	opt = xchg((__force struct ipv6_txoptions **)&inet6_sk(sk)->opt,
11545f6fad8SEric Dumazet 		   opt);
116e7712f1aSYOSHIFUJI Hideaki 	sk_dst_reset(sk);
117e7712f1aSYOSHIFUJI Hideaki 
118e7712f1aSYOSHIFUJI Hideaki 	return opt;
119e7712f1aSYOSHIFUJI Hideaki }
120e7712f1aSYOSHIFUJI Hideaki 
setsockopt_needs_rtnl(int optname)121baf606d9SMarcelo Ricardo Leitner static bool setsockopt_needs_rtnl(int optname)
122baf606d9SMarcelo Ricardo Leitner {
123baf606d9SMarcelo Ricardo Leitner 	switch (optname) {
1248651be8fSWANG Cong 	case IPV6_ADDRFORM:
125baf606d9SMarcelo Ricardo Leitner 	case IPV6_ADD_MEMBERSHIP:
126baf606d9SMarcelo Ricardo Leitner 	case IPV6_DROP_MEMBERSHIP:
127c4a6853dSMarcelo Ricardo Leitner 	case IPV6_JOIN_ANYCAST:
128c4a6853dSMarcelo Ricardo Leitner 	case IPV6_LEAVE_ANYCAST:
129baf606d9SMarcelo Ricardo Leitner 	case MCAST_JOIN_GROUP:
130baf606d9SMarcelo Ricardo Leitner 	case MCAST_LEAVE_GROUP:
13154ff9ef3SMarcelo Ricardo Leitner 	case MCAST_JOIN_SOURCE_GROUP:
13254ff9ef3SMarcelo Ricardo Leitner 	case MCAST_LEAVE_SOURCE_GROUP:
13354ff9ef3SMarcelo Ricardo Leitner 	case MCAST_BLOCK_SOURCE:
13454ff9ef3SMarcelo Ricardo Leitner 	case MCAST_UNBLOCK_SOURCE:
13554ff9ef3SMarcelo Ricardo Leitner 	case MCAST_MSFILTER:
136baf606d9SMarcelo Ricardo Leitner 		return true;
137baf606d9SMarcelo Ricardo Leitner 	}
138baf606d9SMarcelo Ricardo Leitner 	return false;
139baf606d9SMarcelo Ricardo Leitner }
140baf606d9SMarcelo Ricardo Leitner 
copy_group_source_from_sockptr(struct group_source_req * greqs,sockptr_t optval,int optlen)141894cfbc0SChristoph Hellwig static int copy_group_source_from_sockptr(struct group_source_req *greqs,
142894cfbc0SChristoph Hellwig 		sockptr_t optval, int optlen)
143fcfa0b09SAl Viro {
1443021ad52SChristoph Hellwig 	if (in_compat_syscall()) {
1453021ad52SChristoph Hellwig 		struct compat_group_source_req gr32;
146fcfa0b09SAl Viro 
1473021ad52SChristoph Hellwig 		if (optlen < sizeof(gr32))
1483021ad52SChristoph Hellwig 			return -EINVAL;
149894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(&gr32, optval, sizeof(gr32)))
1503021ad52SChristoph Hellwig 			return -EFAULT;
1513021ad52SChristoph Hellwig 		greqs->gsr_interface = gr32.gsr_interface;
1523021ad52SChristoph Hellwig 		greqs->gsr_group = gr32.gsr_group;
1533021ad52SChristoph Hellwig 		greqs->gsr_source = gr32.gsr_source;
1543021ad52SChristoph Hellwig 	} else {
1553021ad52SChristoph Hellwig 		if (optlen < sizeof(*greqs))
1563021ad52SChristoph Hellwig 			return -EINVAL;
157894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(greqs, optval, sizeof(*greqs)))
1583021ad52SChristoph Hellwig 			return -EFAULT;
1593021ad52SChristoph Hellwig 	}
1603021ad52SChristoph Hellwig 
1613021ad52SChristoph Hellwig 	return 0;
1623021ad52SChristoph Hellwig }
1633021ad52SChristoph Hellwig 
do_ipv6_mcast_group_source(struct sock * sk,int optname,sockptr_t optval,int optlen)1643021ad52SChristoph Hellwig static int do_ipv6_mcast_group_source(struct sock *sk, int optname,
165894cfbc0SChristoph Hellwig 		sockptr_t optval, int optlen)
1663021ad52SChristoph Hellwig {
1673021ad52SChristoph Hellwig 	struct group_source_req greqs;
1683021ad52SChristoph Hellwig 	int omode, add;
1693021ad52SChristoph Hellwig 	int ret;
1703021ad52SChristoph Hellwig 
171894cfbc0SChristoph Hellwig 	ret = copy_group_source_from_sockptr(&greqs, optval, optlen);
1723021ad52SChristoph Hellwig 	if (ret)
1733021ad52SChristoph Hellwig 		return ret;
1743021ad52SChristoph Hellwig 
1753021ad52SChristoph Hellwig 	if (greqs.gsr_group.ss_family != AF_INET6 ||
1763021ad52SChristoph Hellwig 	    greqs.gsr_source.ss_family != AF_INET6)
177fcfa0b09SAl Viro 		return -EADDRNOTAVAIL;
178fcfa0b09SAl Viro 
179fcfa0b09SAl Viro 	if (optname == MCAST_BLOCK_SOURCE) {
180fcfa0b09SAl Viro 		omode = MCAST_EXCLUDE;
181fcfa0b09SAl Viro 		add = 1;
182fcfa0b09SAl Viro 	} else if (optname == MCAST_UNBLOCK_SOURCE) {
183fcfa0b09SAl Viro 		omode = MCAST_EXCLUDE;
184fcfa0b09SAl Viro 		add = 0;
185fcfa0b09SAl Viro 	} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
186fcfa0b09SAl Viro 		struct sockaddr_in6 *psin6;
187fcfa0b09SAl Viro 		int retv;
188fcfa0b09SAl Viro 
1893021ad52SChristoph Hellwig 		psin6 = (struct sockaddr_in6 *)&greqs.gsr_group;
1903021ad52SChristoph Hellwig 		retv = ipv6_sock_mc_join_ssm(sk, greqs.gsr_interface,
191fcfa0b09SAl Viro 					     &psin6->sin6_addr,
192fcfa0b09SAl Viro 					     MCAST_INCLUDE);
193fcfa0b09SAl Viro 		/* prior join w/ different source is ok */
194fcfa0b09SAl Viro 		if (retv && retv != -EADDRINUSE)
195fcfa0b09SAl Viro 			return retv;
196fcfa0b09SAl Viro 		omode = MCAST_INCLUDE;
197fcfa0b09SAl Viro 		add = 1;
198fcfa0b09SAl Viro 	} else /* MCAST_LEAVE_SOURCE_GROUP */ {
199fcfa0b09SAl Viro 		omode = MCAST_INCLUDE;
200fcfa0b09SAl Viro 		add = 0;
201fcfa0b09SAl Viro 	}
2023021ad52SChristoph Hellwig 	return ip6_mc_source(add, omode, sk, &greqs);
203fcfa0b09SAl Viro }
204fcfa0b09SAl Viro 
ipv6_set_mcast_msfilter(struct sock * sk,sockptr_t optval,int optlen)205894cfbc0SChristoph Hellwig static int ipv6_set_mcast_msfilter(struct sock *sk, sockptr_t optval,
206ca0e65ebSChristoph Hellwig 		int optlen)
207ca0e65ebSChristoph Hellwig {
208ca0e65ebSChristoph Hellwig 	struct group_filter *gsf;
209ca0e65ebSChristoph Hellwig 	int ret;
210ca0e65ebSChristoph Hellwig 
211ca0e65ebSChristoph Hellwig 	if (optlen < GROUP_FILTER_SIZE(0))
212ca0e65ebSChristoph Hellwig 		return -EINVAL;
2137de6d09fSKuniyuki Iwashima 	if (optlen > READ_ONCE(sysctl_optmem_max))
214ca0e65ebSChristoph Hellwig 		return -ENOBUFS;
215ca0e65ebSChristoph Hellwig 
216894cfbc0SChristoph Hellwig 	gsf = memdup_sockptr(optval, optlen);
217ca0e65ebSChristoph Hellwig 	if (IS_ERR(gsf))
218ca0e65ebSChristoph Hellwig 		return PTR_ERR(gsf);
219ca0e65ebSChristoph Hellwig 
220ca0e65ebSChristoph Hellwig 	/* numsrc >= (4G-140)/128 overflow in 32 bits */
221ca0e65ebSChristoph Hellwig 	ret = -ENOBUFS;
222ca0e65ebSChristoph Hellwig 	if (gsf->gf_numsrc >= 0x1ffffffU ||
223ca0e65ebSChristoph Hellwig 	    gsf->gf_numsrc > sysctl_mld_max_msf)
224ca0e65ebSChristoph Hellwig 		goto out_free_gsf;
225ca0e65ebSChristoph Hellwig 
226ca0e65ebSChristoph Hellwig 	ret = -EINVAL;
227ca0e65ebSChristoph Hellwig 	if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen)
228ca0e65ebSChristoph Hellwig 		goto out_free_gsf;
229ca0e65ebSChristoph Hellwig 
230db243b79SGustavo A. R. Silva 	ret = ip6_mc_msfilter(sk, gsf, gsf->gf_slist_flex);
231ca0e65ebSChristoph Hellwig out_free_gsf:
232ca0e65ebSChristoph Hellwig 	kfree(gsf);
233ca0e65ebSChristoph Hellwig 	return ret;
234ca0e65ebSChristoph Hellwig }
235ca0e65ebSChristoph Hellwig 
compat_ipv6_set_mcast_msfilter(struct sock * sk,sockptr_t optval,int optlen)236894cfbc0SChristoph Hellwig static int compat_ipv6_set_mcast_msfilter(struct sock *sk, sockptr_t optval,
237ca0e65ebSChristoph Hellwig 		int optlen)
238ca0e65ebSChristoph Hellwig {
239db243b79SGustavo A. R. Silva 	const int size0 = offsetof(struct compat_group_filter, gf_slist_flex);
240ca0e65ebSChristoph Hellwig 	struct compat_group_filter *gf32;
241ca0e65ebSChristoph Hellwig 	void *p;
242ca0e65ebSChristoph Hellwig 	int ret;
243ca0e65ebSChristoph Hellwig 	int n;
244ca0e65ebSChristoph Hellwig 
245ca0e65ebSChristoph Hellwig 	if (optlen < size0)
246ca0e65ebSChristoph Hellwig 		return -EINVAL;
2477de6d09fSKuniyuki Iwashima 	if (optlen > READ_ONCE(sysctl_optmem_max) - 4)
248ca0e65ebSChristoph Hellwig 		return -ENOBUFS;
249ca0e65ebSChristoph Hellwig 
250ca0e65ebSChristoph Hellwig 	p = kmalloc(optlen + 4, GFP_KERNEL);
251ca0e65ebSChristoph Hellwig 	if (!p)
252ca0e65ebSChristoph Hellwig 		return -ENOMEM;
253ca0e65ebSChristoph Hellwig 
254db243b79SGustavo A. R. Silva 	gf32 = p + 4; /* we want ->gf_group and ->gf_slist_flex aligned */
255ca0e65ebSChristoph Hellwig 	ret = -EFAULT;
256894cfbc0SChristoph Hellwig 	if (copy_from_sockptr(gf32, optval, optlen))
257ca0e65ebSChristoph Hellwig 		goto out_free_p;
258ca0e65ebSChristoph Hellwig 
259ca0e65ebSChristoph Hellwig 	/* numsrc >= (4G-140)/128 overflow in 32 bits */
260ca0e65ebSChristoph Hellwig 	ret = -ENOBUFS;
261ca0e65ebSChristoph Hellwig 	n = gf32->gf_numsrc;
262ca0e65ebSChristoph Hellwig 	if (n >= 0x1ffffffU || n > sysctl_mld_max_msf)
263ca0e65ebSChristoph Hellwig 		goto out_free_p;
264ca0e65ebSChristoph Hellwig 
265ca0e65ebSChristoph Hellwig 	ret = -EINVAL;
266db243b79SGustavo A. R. Silva 	if (offsetof(struct compat_group_filter, gf_slist_flex[n]) > optlen)
267ca0e65ebSChristoph Hellwig 		goto out_free_p;
268ca0e65ebSChristoph Hellwig 
269ca0e65ebSChristoph Hellwig 	ret = ip6_mc_msfilter(sk, &(struct group_filter){
270ca0e65ebSChristoph Hellwig 			.gf_interface = gf32->gf_interface,
271ca0e65ebSChristoph Hellwig 			.gf_group = gf32->gf_group,
272ca0e65ebSChristoph Hellwig 			.gf_fmode = gf32->gf_fmode,
273db243b79SGustavo A. R. Silva 			.gf_numsrc = gf32->gf_numsrc}, gf32->gf_slist_flex);
274ca0e65ebSChristoph Hellwig 
275ca0e65ebSChristoph Hellwig out_free_p:
276ca0e65ebSChristoph Hellwig 	kfree(p);
277ca0e65ebSChristoph Hellwig 	return ret;
278ca0e65ebSChristoph Hellwig }
279ca0e65ebSChristoph Hellwig 
ipv6_mcast_join_leave(struct sock * sk,int optname,sockptr_t optval,int optlen)280fdf5bdd8SChristoph Hellwig static int ipv6_mcast_join_leave(struct sock *sk, int optname,
281894cfbc0SChristoph Hellwig 		sockptr_t optval, int optlen)
282fdf5bdd8SChristoph Hellwig {
283fdf5bdd8SChristoph Hellwig 	struct sockaddr_in6 *psin6;
284fdf5bdd8SChristoph Hellwig 	struct group_req greq;
285fdf5bdd8SChristoph Hellwig 
286fdf5bdd8SChristoph Hellwig 	if (optlen < sizeof(greq))
287fdf5bdd8SChristoph Hellwig 		return -EINVAL;
288894cfbc0SChristoph Hellwig 	if (copy_from_sockptr(&greq, optval, sizeof(greq)))
289fdf5bdd8SChristoph Hellwig 		return -EFAULT;
290fdf5bdd8SChristoph Hellwig 
291fdf5bdd8SChristoph Hellwig 	if (greq.gr_group.ss_family != AF_INET6)
292fdf5bdd8SChristoph Hellwig 		return -EADDRNOTAVAIL;
293fdf5bdd8SChristoph Hellwig 	psin6 = (struct sockaddr_in6 *)&greq.gr_group;
294fdf5bdd8SChristoph Hellwig 	if (optname == MCAST_JOIN_GROUP)
295fdf5bdd8SChristoph Hellwig 		return ipv6_sock_mc_join(sk, greq.gr_interface,
296fdf5bdd8SChristoph Hellwig 					 &psin6->sin6_addr);
297fdf5bdd8SChristoph Hellwig 	return ipv6_sock_mc_drop(sk, greq.gr_interface, &psin6->sin6_addr);
298fdf5bdd8SChristoph Hellwig }
299fdf5bdd8SChristoph Hellwig 
compat_ipv6_mcast_join_leave(struct sock * sk,int optname,sockptr_t optval,int optlen)300fdf5bdd8SChristoph Hellwig static int compat_ipv6_mcast_join_leave(struct sock *sk, int optname,
301894cfbc0SChristoph Hellwig 		sockptr_t optval, int optlen)
302fdf5bdd8SChristoph Hellwig {
303fdf5bdd8SChristoph Hellwig 	struct compat_group_req gr32;
304fdf5bdd8SChristoph Hellwig 	struct sockaddr_in6 *psin6;
305fdf5bdd8SChristoph Hellwig 
306fdf5bdd8SChristoph Hellwig 	if (optlen < sizeof(gr32))
307fdf5bdd8SChristoph Hellwig 		return -EINVAL;
308894cfbc0SChristoph Hellwig 	if (copy_from_sockptr(&gr32, optval, sizeof(gr32)))
309fdf5bdd8SChristoph Hellwig 		return -EFAULT;
310fdf5bdd8SChristoph Hellwig 
311fdf5bdd8SChristoph Hellwig 	if (gr32.gr_group.ss_family != AF_INET6)
312fdf5bdd8SChristoph Hellwig 		return -EADDRNOTAVAIL;
313fdf5bdd8SChristoph Hellwig 	psin6 = (struct sockaddr_in6 *)&gr32.gr_group;
314fdf5bdd8SChristoph Hellwig 	if (optname == MCAST_JOIN_GROUP)
3153021ad52SChristoph Hellwig 		return ipv6_sock_mc_join(sk, gr32.gr_interface,
316fdf5bdd8SChristoph Hellwig 					&psin6->sin6_addr);
3173021ad52SChristoph Hellwig 	return ipv6_sock_mc_drop(sk, gr32.gr_interface, &psin6->sin6_addr);
318fdf5bdd8SChristoph Hellwig }
319fdf5bdd8SChristoph Hellwig 
ipv6_set_opt_hdr(struct sock * sk,int optname,sockptr_t optval,int optlen)320894cfbc0SChristoph Hellwig static int ipv6_set_opt_hdr(struct sock *sk, int optname, sockptr_t optval,
321b84d2b73SChristoph Hellwig 		int optlen)
322b84d2b73SChristoph Hellwig {
323b84d2b73SChristoph Hellwig 	struct ipv6_pinfo *np = inet6_sk(sk);
324b84d2b73SChristoph Hellwig 	struct ipv6_opt_hdr *new = NULL;
325b84d2b73SChristoph Hellwig 	struct net *net = sock_net(sk);
326b84d2b73SChristoph Hellwig 	struct ipv6_txoptions *opt;
327b84d2b73SChristoph Hellwig 	int err;
328b84d2b73SChristoph Hellwig 
329b84d2b73SChristoph Hellwig 	/* hop-by-hop / destination options are privileged option */
33040cd308eSMartin KaFai Lau 	if (optname != IPV6_RTHDR && !sockopt_ns_capable(net->user_ns, CAP_NET_RAW))
331b84d2b73SChristoph Hellwig 		return -EPERM;
332b84d2b73SChristoph Hellwig 
333b84d2b73SChristoph Hellwig 	/* remove any sticky options header with a zero option
334b84d2b73SChristoph Hellwig 	 * length, per RFC3542.
335b84d2b73SChristoph Hellwig 	 */
336b84d2b73SChristoph Hellwig 	if (optlen > 0) {
337894cfbc0SChristoph Hellwig 		if (sockptr_is_null(optval))
338b84d2b73SChristoph Hellwig 			return -EINVAL;
339b84d2b73SChristoph Hellwig 		if (optlen < sizeof(struct ipv6_opt_hdr) ||
340b84d2b73SChristoph Hellwig 		    optlen & 0x7 ||
341b84d2b73SChristoph Hellwig 		    optlen > 8 * 255)
342b84d2b73SChristoph Hellwig 			return -EINVAL;
343b84d2b73SChristoph Hellwig 
344894cfbc0SChristoph Hellwig 		new = memdup_sockptr(optval, optlen);
345b84d2b73SChristoph Hellwig 		if (IS_ERR(new))
346b84d2b73SChristoph Hellwig 			return PTR_ERR(new);
347b84d2b73SChristoph Hellwig 		if (unlikely(ipv6_optlen(new) > optlen)) {
348b84d2b73SChristoph Hellwig 			kfree(new);
349b84d2b73SChristoph Hellwig 			return -EINVAL;
350b84d2b73SChristoph Hellwig 		}
351b84d2b73SChristoph Hellwig 	}
352b84d2b73SChristoph Hellwig 
353b84d2b73SChristoph Hellwig 	opt = rcu_dereference_protected(np->opt, lockdep_sock_is_held(sk));
354b84d2b73SChristoph Hellwig 	opt = ipv6_renew_options(sk, opt, optname, new);
355b84d2b73SChristoph Hellwig 	kfree(new);
356b84d2b73SChristoph Hellwig 	if (IS_ERR(opt))
357b84d2b73SChristoph Hellwig 		return PTR_ERR(opt);
358b84d2b73SChristoph Hellwig 
359b84d2b73SChristoph Hellwig 	/* routing header option needs extra check */
360b84d2b73SChristoph Hellwig 	err = -EINVAL;
361b84d2b73SChristoph Hellwig 	if (optname == IPV6_RTHDR && opt && opt->srcrt) {
362b84d2b73SChristoph Hellwig 		struct ipv6_rt_hdr *rthdr = opt->srcrt;
363b84d2b73SChristoph Hellwig 		switch (rthdr->type) {
364b84d2b73SChristoph Hellwig #if IS_ENABLED(CONFIG_IPV6_MIP6)
365b84d2b73SChristoph Hellwig 		case IPV6_SRCRT_TYPE_2:
366b84d2b73SChristoph Hellwig 			if (rthdr->hdrlen != 2 || rthdr->segments_left != 1)
367b84d2b73SChristoph Hellwig 				goto sticky_done;
368b84d2b73SChristoph Hellwig 			break;
369b84d2b73SChristoph Hellwig #endif
370b84d2b73SChristoph Hellwig 		case IPV6_SRCRT_TYPE_4:
371b84d2b73SChristoph Hellwig 		{
372b84d2b73SChristoph Hellwig 			struct ipv6_sr_hdr *srh =
373b84d2b73SChristoph Hellwig 				(struct ipv6_sr_hdr *)opt->srcrt;
374b84d2b73SChristoph Hellwig 
375b84d2b73SChristoph Hellwig 			if (!seg6_validate_srh(srh, optlen, false))
376b84d2b73SChristoph Hellwig 				goto sticky_done;
377b84d2b73SChristoph Hellwig 			break;
378b84d2b73SChristoph Hellwig 		}
379b84d2b73SChristoph Hellwig 		default:
380b84d2b73SChristoph Hellwig 			goto sticky_done;
381b84d2b73SChristoph Hellwig 		}
382b84d2b73SChristoph Hellwig 	}
383b84d2b73SChristoph Hellwig 
384b84d2b73SChristoph Hellwig 	err = 0;
385b84d2b73SChristoph Hellwig 	opt = ipv6_update_options(sk, opt);
386b84d2b73SChristoph Hellwig sticky_done:
387b84d2b73SChristoph Hellwig 	if (opt) {
388b84d2b73SChristoph Hellwig 		atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
389b84d2b73SChristoph Hellwig 		txopt_put(opt);
390b84d2b73SChristoph Hellwig 	}
391b84d2b73SChristoph Hellwig 	return err;
392b84d2b73SChristoph Hellwig }
393b84d2b73SChristoph Hellwig 
do_ipv6_setsockopt(struct sock * sk,int level,int optname,sockptr_t optval,unsigned int optlen)39475b64b68SMartin KaFai Lau int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
395894cfbc0SChristoph Hellwig 		       sockptr_t optval, unsigned int optlen)
3961da177e4SLinus Torvalds {
3971da177e4SLinus Torvalds 	struct ipv6_pinfo *np = inet6_sk(sk);
3983b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(sk);
3991da177e4SLinus Torvalds 	int val, valbool;
4001da177e4SLinus Torvalds 	int retv = -ENOPROTOOPT;
401baf606d9SMarcelo Ricardo Leitner 	bool needs_rtnl = setsockopt_needs_rtnl(optname);
4021da177e4SLinus Torvalds 
403894cfbc0SChristoph Hellwig 	if (sockptr_is_null(optval))
4041da177e4SLinus Torvalds 		val = 0;
405b2a9d7c2SYOSHIFUJI Hideaki 	else {
406b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen >= sizeof(int)) {
407894cfbc0SChristoph Hellwig 			if (copy_from_sockptr(&val, optval, sizeof(val)))
4081da177e4SLinus Torvalds 				return -EFAULT;
409b2a9d7c2SYOSHIFUJI Hideaki 		} else
410b2a9d7c2SYOSHIFUJI Hideaki 			val = 0;
411b2a9d7c2SYOSHIFUJI Hideaki 	}
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 	valbool = (val != 0);
4141da177e4SLinus Torvalds 
4157bc570c8SYOSHIFUJI Hideaki 	if (ip6_mroute_opt(optname))
416894cfbc0SChristoph Hellwig 		return ip6_mroute_setsockopt(sk, optname, optval, optlen);
4177bc570c8SYOSHIFUJI Hideaki 
418baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
419baf606d9SMarcelo Ricardo Leitner 		rtnl_lock();
42040cd308eSMartin KaFai Lau 	sockopt_lock_sock(sk);
4211da177e4SLinus Torvalds 
4223c52c6bbSKuniyuki Iwashima 	/* Another thread has converted the socket into IPv4 with
4233c52c6bbSKuniyuki Iwashima 	 * IPV6_ADDRFORM concurrently.
4243c52c6bbSKuniyuki Iwashima 	 */
4253c52c6bbSKuniyuki Iwashima 	if (unlikely(sk->sk_family != AF_INET6))
4263c52c6bbSKuniyuki Iwashima 		goto unlock;
4273c52c6bbSKuniyuki Iwashima 
4281da177e4SLinus Torvalds 	switch (optname) {
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 	case IPV6_ADDRFORM:
431b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
432b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
4331da177e4SLinus Torvalds 		if (val == PF_INET) {
43449d074f4SDenis V. Lunev 			if (sk->sk_type == SOCK_RAW)
43549d074f4SDenis V. Lunev 				break;
43649d074f4SDenis V. Lunev 
4379596cc82SDenis V. Lunev 			if (sk->sk_protocol == IPPROTO_UDP ||
4389596cc82SDenis V. Lunev 			    sk->sk_protocol == IPPROTO_UDPLITE) {
4399596cc82SDenis V. Lunev 				struct udp_sock *up = udp_sk(sk);
4409596cc82SDenis V. Lunev 				if (up->pending == AF_INET6) {
4419596cc82SDenis V. Lunev 					retv = -EBUSY;
4429596cc82SDenis V. Lunev 					break;
4439596cc82SDenis V. Lunev 				}
44479a1f0ccSHangbin Liu 			} else if (sk->sk_protocol == IPPROTO_TCP) {
44579a1f0ccSHangbin Liu 				if (sk->sk_prot != &tcpv6_prot) {
446b6f61189SEric Dumazet 					retv = -EBUSY;
4471da177e4SLinus Torvalds 					break;
448b6f61189SEric Dumazet 				}
44979a1f0ccSHangbin Liu 			} else {
450b6f61189SEric Dumazet 				break;
45179a1f0ccSHangbin Liu 			}
45279a1f0ccSHangbin Liu 
4531da177e4SLinus Torvalds 			if (sk->sk_state != TCP_ESTABLISHED) {
4541da177e4SLinus Torvalds 				retv = -ENOTCONN;
4551da177e4SLinus Torvalds 				break;
4561da177e4SLinus Torvalds 			}
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds 			if (ipv6_only_sock(sk) ||
459efe4208fSEric Dumazet 			    !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) {
4601da177e4SLinus Torvalds 				retv = -EADDRNOTAVAIL;
4611da177e4SLinus Torvalds 				break;
4621da177e4SLinus Torvalds 			}
4631da177e4SLinus Torvalds 
4648651be8fSWANG Cong 			__ipv6_sock_mc_close(sk);
4658c0de6e9SCong Wang 			__ipv6_sock_ac_close(sk);
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds 			if (sk->sk_protocol == IPPROTO_TCP) {
468d83d8461SArnaldo Carvalho de Melo 				struct inet_connection_sock *icsk = inet_csk(sk);
469b3cb764aSEric Dumazet 
470c29a0bc4SPavel Emelyanov 				sock_prot_inuse_add(net, sk->sk_prot, -1);
471c29a0bc4SPavel Emelyanov 				sock_prot_inuse_add(net, &tcp_prot, 1);
472b3cb764aSEric Dumazet 
473364f997bSKuniyuki Iwashima 				/* Paired with READ_ONCE(sk->sk_prot) in inet6_stream_ops */
474086d4905SEric Dumazet 				WRITE_ONCE(sk->sk_prot, &tcp_prot);
475f49cd2f4SKuniyuki Iwashima 				/* Paired with READ_ONCE() in tcp_(get|set)sockopt() */
476f49cd2f4SKuniyuki Iwashima 				WRITE_ONCE(icsk->icsk_af_ops, &ipv4_specific);
4771ded5e5aSEric Dumazet 				WRITE_ONCE(sk->sk_socket->ops, &inet_stream_ops);
4781ded5e5aSEric Dumazet 				WRITE_ONCE(sk->sk_family, PF_INET);
479d83d8461SArnaldo Carvalho de Melo 				tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
4801da177e4SLinus Torvalds 			} else {
481ba4e58ecSGerrit Renker 				struct proto *prot = &udp_prot;
482ba4e58ecSGerrit Renker 
483db8dac20SDavid S. Miller 				if (sk->sk_protocol == IPPROTO_UDPLITE)
484ba4e58ecSGerrit Renker 					prot = &udplite_prot;
485b3cb764aSEric Dumazet 
486c29a0bc4SPavel Emelyanov 				sock_prot_inuse_add(net, sk->sk_prot, -1);
487c29a0bc4SPavel Emelyanov 				sock_prot_inuse_add(net, prot, 1);
488b3cb764aSEric Dumazet 
489364f997bSKuniyuki Iwashima 				/* Paired with READ_ONCE(sk->sk_prot) in inet6_dgram_ops */
490086d4905SEric Dumazet 				WRITE_ONCE(sk->sk_prot, prot);
4911ded5e5aSEric Dumazet 				WRITE_ONCE(sk->sk_socket->ops, &inet_dgram_ops);
4921ded5e5aSEric Dumazet 				WRITE_ONCE(sk->sk_family, PF_INET);
4931da177e4SLinus Torvalds 			}
49421985f43SKuniyuki Iwashima 
49521985f43SKuniyuki Iwashima 			/* Disable all options not to allocate memory anymore,
49621985f43SKuniyuki Iwashima 			 * but there is still a race.  See the lockless path
49721985f43SKuniyuki Iwashima 			 * in udpv6_sendmsg() and ipv6_local_rxpmtu().
49821985f43SKuniyuki Iwashima 			 */
49921985f43SKuniyuki Iwashima 			np->rxopt.all = 0;
50021985f43SKuniyuki Iwashima 
50121985f43SKuniyuki Iwashima 			inet6_cleanup_sock(sk);
5021da177e4SLinus Torvalds 
5031da177e4SLinus Torvalds 			module_put(THIS_MODULE);
5041da177e4SLinus Torvalds 			retv = 0;
5051da177e4SLinus Torvalds 			break;
5061da177e4SLinus Torvalds 		}
5071da177e4SLinus Torvalds 		goto e_inval;
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds 	case IPV6_V6ONLY:
510b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int) ||
511c720c7e8SEric Dumazet 		    inet_sk(sk)->inet_num)
5121da177e4SLinus Torvalds 			goto e_inval;
5139fe516baSEric Dumazet 		sk->sk_ipv6only = valbool;
5141da177e4SLinus Torvalds 		retv = 0;
5151da177e4SLinus Torvalds 		break;
5161da177e4SLinus Torvalds 
517333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVPKTINFO:
518b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
519b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5201da177e4SLinus Torvalds 		np->rxopt.bits.rxinfo = valbool;
5211da177e4SLinus Torvalds 		retv = 0;
5221da177e4SLinus Torvalds 		break;
5231da177e4SLinus Torvalds 
524333fad53SYOSHIFUJI Hideaki 	case IPV6_2292PKTINFO:
525b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
526b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
527333fad53SYOSHIFUJI Hideaki 		np->rxopt.bits.rxoinfo = valbool;
528333fad53SYOSHIFUJI Hideaki 		retv = 0;
529333fad53SYOSHIFUJI Hideaki 		break;
530333fad53SYOSHIFUJI Hideaki 
531333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVHOPLIMIT:
532b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
533b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5341da177e4SLinus Torvalds 		np->rxopt.bits.rxhlim = valbool;
5351da177e4SLinus Torvalds 		retv = 0;
5361da177e4SLinus Torvalds 		break;
5371da177e4SLinus Torvalds 
538333fad53SYOSHIFUJI Hideaki 	case IPV6_2292HOPLIMIT:
539b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
540b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
541333fad53SYOSHIFUJI Hideaki 		np->rxopt.bits.rxohlim = valbool;
542333fad53SYOSHIFUJI Hideaki 		retv = 0;
543333fad53SYOSHIFUJI Hideaki 		break;
544333fad53SYOSHIFUJI Hideaki 
545333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVRTHDR:
546b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
547b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5484c752098SYOSHIFUJI Hideaki 		np->rxopt.bits.srcrt = valbool;
5491da177e4SLinus Torvalds 		retv = 0;
5501da177e4SLinus Torvalds 		break;
5511da177e4SLinus Torvalds 
552333fad53SYOSHIFUJI Hideaki 	case IPV6_2292RTHDR:
553b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
554b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5554c752098SYOSHIFUJI Hideaki 		np->rxopt.bits.osrcrt = valbool;
556333fad53SYOSHIFUJI Hideaki 		retv = 0;
557333fad53SYOSHIFUJI Hideaki 		break;
558333fad53SYOSHIFUJI Hideaki 
559333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVHOPOPTS:
560b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
561b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5621da177e4SLinus Torvalds 		np->rxopt.bits.hopopts = valbool;
5631da177e4SLinus Torvalds 		retv = 0;
5641da177e4SLinus Torvalds 		break;
5651da177e4SLinus Torvalds 
566333fad53SYOSHIFUJI Hideaki 	case IPV6_2292HOPOPTS:
567b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
568b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
569333fad53SYOSHIFUJI Hideaki 		np->rxopt.bits.ohopopts = valbool;
570333fad53SYOSHIFUJI Hideaki 		retv = 0;
571333fad53SYOSHIFUJI Hideaki 		break;
572333fad53SYOSHIFUJI Hideaki 
573333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVDSTOPTS:
574b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
575b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5761da177e4SLinus Torvalds 		np->rxopt.bits.dstopts = valbool;
5771da177e4SLinus Torvalds 		retv = 0;
5781da177e4SLinus Torvalds 		break;
5791da177e4SLinus Torvalds 
580333fad53SYOSHIFUJI Hideaki 	case IPV6_2292DSTOPTS:
581b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
582b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
583333fad53SYOSHIFUJI Hideaki 		np->rxopt.bits.odstopts = valbool;
584333fad53SYOSHIFUJI Hideaki 		retv = 0;
585333fad53SYOSHIFUJI Hideaki 		break;
586333fad53SYOSHIFUJI Hideaki 
58741a1f8eaSYOSHIFUJI Hideaki 	case IPV6_TCLASS:
588b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
589b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
590d0ee011fSRemi Denis-Courmont 		if (val < -1 || val > 0xff)
59141a1f8eaSYOSHIFUJI Hideaki 			goto e_inval;
59226ced1e4SGerrit Renker 		/* RFC 3542, 6.5: default traffic class of 0x0 */
59326ced1e4SGerrit Renker 		if (val == -1)
59426ced1e4SGerrit Renker 			val = 0;
5959f7b3a69SMaciej Żenczykowski 		if (sk->sk_type == SOCK_STREAM) {
5969f7b3a69SMaciej Żenczykowski 			val &= ~INET_ECN_MASK;
5979f7b3a69SMaciej Żenczykowski 			val |= np->tclass & INET_ECN_MASK;
5989f7b3a69SMaciej Żenczykowski 		}
599305e95bbSMaciej Żenczykowski 		if (np->tclass != val) {
60041a1f8eaSYOSHIFUJI Hideaki 			np->tclass = val;
601305e95bbSMaciej Żenczykowski 			sk_dst_reset(sk);
602305e95bbSMaciej Żenczykowski 		}
60341a1f8eaSYOSHIFUJI Hideaki 		retv = 0;
60441a1f8eaSYOSHIFUJI Hideaki 		break;
60541a1f8eaSYOSHIFUJI Hideaki 
60641a1f8eaSYOSHIFUJI Hideaki 	case IPV6_RECVTCLASS:
607b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
608b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
60941a1f8eaSYOSHIFUJI Hideaki 		np->rxopt.bits.rxtclass = valbool;
61041a1f8eaSYOSHIFUJI Hideaki 		retv = 0;
61141a1f8eaSYOSHIFUJI Hideaki 		break;
61241a1f8eaSYOSHIFUJI Hideaki 
6131da177e4SLinus Torvalds 	case IPV6_FLOWINFO:
614b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
615b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
6161da177e4SLinus Torvalds 		np->rxopt.bits.rxflow = valbool;
6171da177e4SLinus Torvalds 		retv = 0;
6181da177e4SLinus Torvalds 		break;
6191da177e4SLinus Torvalds 
620793b1473SBrian Haley 	case IPV6_RECVPATHMTU:
621793b1473SBrian Haley 		if (optlen < sizeof(int))
622793b1473SBrian Haley 			goto e_inval;
623793b1473SBrian Haley 		np->rxopt.bits.rxpmtu = valbool;
624793b1473SBrian Haley 		retv = 0;
625793b1473SBrian Haley 		break;
626793b1473SBrian Haley 
6276c468622SBalazs Scheidler 	case IPV6_TRANSPARENT:
62840cd308eSMartin KaFai Lau 		if (valbool && !sockopt_ns_capable(net->user_ns, CAP_NET_RAW) &&
62940cd308eSMartin KaFai Lau 		    !sockopt_ns_capable(net->user_ns, CAP_NET_ADMIN)) {
630b889416bSBalazs Scheidler 			retv = -EPERM;
631b889416bSBalazs Scheidler 			break;
632b889416bSBalazs Scheidler 		}
6336c468622SBalazs Scheidler 		if (optlen < sizeof(int))
6346c468622SBalazs Scheidler 			goto e_inval;
6356c468622SBalazs Scheidler 		/* we don't have a separate transparent bit for IPV6 we use the one in the IPv4 socket */
6364bd0623fSEric Dumazet 		inet_assign_bit(TRANSPARENT, sk, valbool);
6376c468622SBalazs Scheidler 		retv = 0;
6386c468622SBalazs Scheidler 		break;
6396c468622SBalazs Scheidler 
64084e14fe3SMaciej Żenczykowski 	case IPV6_FREEBIND:
64184e14fe3SMaciej Żenczykowski 		if (optlen < sizeof(int))
64284e14fe3SMaciej Żenczykowski 			goto e_inval;
64384e14fe3SMaciej Żenczykowski 		/* we also don't have a separate freebind bit for IPV6 */
6443f7e7532SEric Dumazet 		inet_assign_bit(FREEBIND, sk, valbool);
64584e14fe3SMaciej Żenczykowski 		retv = 0;
64684e14fe3SMaciej Żenczykowski 		break;
64784e14fe3SMaciej Żenczykowski 
6486c468622SBalazs Scheidler 	case IPV6_RECVORIGDSTADDR:
6496c468622SBalazs Scheidler 		if (optlen < sizeof(int))
6506c468622SBalazs Scheidler 			goto e_inval;
6516c468622SBalazs Scheidler 		np->rxopt.bits.rxorigdstaddr = valbool;
6526c468622SBalazs Scheidler 		retv = 0;
6536c468622SBalazs Scheidler 		break;
6546c468622SBalazs Scheidler 
655333fad53SYOSHIFUJI Hideaki 	case IPV6_HOPOPTS:
656333fad53SYOSHIFUJI Hideaki 	case IPV6_RTHDRDSTOPTS:
657333fad53SYOSHIFUJI Hideaki 	case IPV6_RTHDR:
658333fad53SYOSHIFUJI Hideaki 	case IPV6_DSTOPTS:
659b84d2b73SChristoph Hellwig 		retv = ipv6_set_opt_hdr(sk, optname, optval, optlen);
660a9ba23d4SPaul Moore 		break;
661aea7427fSShan Wei 
662b24a2516SYang Hongyang 	case IPV6_PKTINFO:
663b24a2516SYang Hongyang 	{
664b24a2516SYang Hongyang 		struct in6_pktinfo pkt;
665b24a2516SYang Hongyang 
666b24a2516SYang Hongyang 		if (optlen == 0)
667b24a2516SYang Hongyang 			goto e_inval;
668894cfbc0SChristoph Hellwig 		else if (optlen < sizeof(struct in6_pktinfo) ||
669894cfbc0SChristoph Hellwig 			 sockptr_is_null(optval))
670b24a2516SYang Hongyang 			goto e_inval;
671b24a2516SYang Hongyang 
672894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(&pkt, optval, sizeof(pkt))) {
673b24a2516SYang Hongyang 			retv = -EFAULT;
674b24a2516SYang Hongyang 			break;
675b24a2516SYang Hongyang 		}
676d839a0ebSMike Manning 		if (!sk_dev_equal_l3scope(sk, pkt.ipi6_ifindex))
677b24a2516SYang Hongyang 			goto e_inval;
678b24a2516SYang Hongyang 
679b24a2516SYang Hongyang 		np->sticky_pktinfo.ipi6_ifindex = pkt.ipi6_ifindex;
6804e3fd7a0SAlexey Dobriyan 		np->sticky_pktinfo.ipi6_addr = pkt.ipi6_addr;
681b24a2516SYang Hongyang 		retv = 0;
682b24a2516SYang Hongyang 		break;
683b24a2516SYang Hongyang 	}
684b24a2516SYang Hongyang 
685333fad53SYOSHIFUJI Hideaki 	case IPV6_2292PKTOPTIONS:
6861da177e4SLinus Torvalds 	{
6871da177e4SLinus Torvalds 		struct ipv6_txoptions *opt = NULL;
6881da177e4SLinus Torvalds 		struct msghdr msg;
6894c9483b2SDavid S. Miller 		struct flowi6 fl6;
69026879da5SWei Wang 		struct ipcm6_cookie ipc6;
6911da177e4SLinus Torvalds 
6924c9483b2SDavid S. Miller 		memset(&fl6, 0, sizeof(fl6));
6934c9483b2SDavid S. Miller 		fl6.flowi6_oif = sk->sk_bound_dev_if;
6944c9483b2SDavid S. Miller 		fl6.flowi6_mark = sk->sk_mark;
6951da177e4SLinus Torvalds 
6961da177e4SLinus Torvalds 		if (optlen == 0)
6971da177e4SLinus Torvalds 			goto update;
6981da177e4SLinus Torvalds 
6991da177e4SLinus Torvalds 		/* 1K is probably excessive
7001da177e4SLinus Torvalds 		 * 1K is surely not enough, 2K per standard header is 16K.
7011da177e4SLinus Torvalds 		 */
7021da177e4SLinus Torvalds 		retv = -EINVAL;
7031da177e4SLinus Torvalds 		if (optlen > 64*1024)
7041da177e4SLinus Torvalds 			break;
7051da177e4SLinus Torvalds 
7061da177e4SLinus Torvalds 		opt = sock_kmalloc(sk, sizeof(*opt) + optlen, GFP_KERNEL);
7071da177e4SLinus Torvalds 		retv = -ENOBUFS;
70863159f29SIan Morris 		if (!opt)
7091da177e4SLinus Torvalds 			break;
7101da177e4SLinus Torvalds 
7111da177e4SLinus Torvalds 		memset(opt, 0, sizeof(*opt));
7120aeea21aSReshetova, Elena 		refcount_set(&opt->refcnt, 1);
7131da177e4SLinus Torvalds 		opt->tot_len = sizeof(*opt) + optlen;
7141da177e4SLinus Torvalds 		retv = -EFAULT;
715894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(opt + 1, optval, optlen))
7161da177e4SLinus Torvalds 			goto done;
7171da177e4SLinus Torvalds 
7181da177e4SLinus Torvalds 		msg.msg_controllen = optlen;
719b6d85cf5SKevin Brodsky 		msg.msg_control_is_user = false;
7201da177e4SLinus Torvalds 		msg.msg_control = (void *)(opt+1);
72126879da5SWei Wang 		ipc6.opt = opt;
7221da177e4SLinus Torvalds 
7235fdaa88dSWillem de Bruijn 		retv = ip6_datagram_send_ctl(net, sk, &msg, &fl6, &ipc6);
7241da177e4SLinus Torvalds 		if (retv)
7251da177e4SLinus Torvalds 			goto done;
7261da177e4SLinus Torvalds update:
7271da177e4SLinus Torvalds 		retv = 0;
728e7712f1aSYOSHIFUJI Hideaki 		opt = ipv6_update_options(sk, opt);
7291da177e4SLinus Torvalds done:
73045f6fad8SEric Dumazet 		if (opt) {
73145f6fad8SEric Dumazet 			atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
73245f6fad8SEric Dumazet 			txopt_put(opt);
73345f6fad8SEric Dumazet 		}
7341da177e4SLinus Torvalds 		break;
7351da177e4SLinus Torvalds 	}
7361da177e4SLinus Torvalds 	case IPV6_UNICAST_HOPS:
737b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
738b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
7391da177e4SLinus Torvalds 		if (val > 255 || val < -1)
7401da177e4SLinus Torvalds 			goto e_inval;
7411da177e4SLinus Torvalds 		np->hop_limit = val;
7421da177e4SLinus Torvalds 		retv = 0;
7431da177e4SLinus Torvalds 		break;
7441da177e4SLinus Torvalds 
7451da177e4SLinus Torvalds 	case IPV6_MULTICAST_HOPS:
7461da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
7471717699cSYOSHIFUJI Hideaki 			break;
748b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
749b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
7501da177e4SLinus Torvalds 		if (val > 255 || val < -1)
7511da177e4SLinus Torvalds 			goto e_inval;
7522a38e6d5SLi Wei 		np->mcast_hops = (val == -1 ? IPV6_DEFAULT_MCASTHOPS : val);
7531da177e4SLinus Torvalds 		retv = 0;
7541da177e4SLinus Torvalds 		break;
7551da177e4SLinus Torvalds 
7561da177e4SLinus Torvalds 	case IPV6_MULTICAST_LOOP:
757b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
758b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
75928d44882SYOSHIFUJI Hideaki 		if (val != valbool)
76028d44882SYOSHIFUJI Hideaki 			goto e_inval;
7611da177e4SLinus Torvalds 		np->mc_loop = valbool;
7621da177e4SLinus Torvalds 		retv = 0;
7631da177e4SLinus Torvalds 		break;
7641da177e4SLinus Torvalds 
765c4062dfcSErich E. Hoover 	case IPV6_UNICAST_IF:
766c4062dfcSErich E. Hoover 	{
767c4062dfcSErich E. Hoover 		struct net_device *dev = NULL;
768c4062dfcSErich E. Hoover 		int ifindex;
769c4062dfcSErich E. Hoover 
770c4062dfcSErich E. Hoover 		if (optlen != sizeof(int))
771c4062dfcSErich E. Hoover 			goto e_inval;
772c4062dfcSErich E. Hoover 
773c4062dfcSErich E. Hoover 		ifindex = (__force int)ntohl((__force __be32)val);
774c4062dfcSErich E. Hoover 		if (ifindex == 0) {
775c4062dfcSErich E. Hoover 			np->ucast_oif = 0;
776c4062dfcSErich E. Hoover 			retv = 0;
777c4062dfcSErich E. Hoover 			break;
778c4062dfcSErich E. Hoover 		}
779c4062dfcSErich E. Hoover 
780c4062dfcSErich E. Hoover 		dev = dev_get_by_index(net, ifindex);
781c4062dfcSErich E. Hoover 		retv = -EADDRNOTAVAIL;
782c4062dfcSErich E. Hoover 		if (!dev)
783c4062dfcSErich E. Hoover 			break;
784c4062dfcSErich E. Hoover 		dev_put(dev);
785c4062dfcSErich E. Hoover 
786c4062dfcSErich E. Hoover 		retv = -EINVAL;
787c4062dfcSErich E. Hoover 		if (sk->sk_bound_dev_if)
788c4062dfcSErich E. Hoover 			break;
789c4062dfcSErich E. Hoover 
790c4062dfcSErich E. Hoover 		np->ucast_oif = ifindex;
791c4062dfcSErich E. Hoover 		retv = 0;
792c4062dfcSErich E. Hoover 		break;
793c4062dfcSErich E. Hoover 	}
794c4062dfcSErich E. Hoover 
7951da177e4SLinus Torvalds 	case IPV6_MULTICAST_IF:
7961da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
7971717699cSYOSHIFUJI Hideaki 			break;
798b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
799b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
8004953f0fcSBrian Haley 
8014953f0fcSBrian Haley 		if (val) {
80255b80503SEric Dumazet 			struct net_device *dev;
8037bb387c5SDavid Ahern 			int midx;
80455b80503SEric Dumazet 
8057bb387c5SDavid Ahern 			rcu_read_lock();
8061da177e4SLinus Torvalds 
8077bb387c5SDavid Ahern 			dev = dev_get_by_index_rcu(net, val);
80855b80503SEric Dumazet 			if (!dev) {
8097bb387c5SDavid Ahern 				rcu_read_unlock();
8101da177e4SLinus Torvalds 				retv = -ENODEV;
8111da177e4SLinus Torvalds 				break;
8121da177e4SLinus Torvalds 			}
8137bb387c5SDavid Ahern 			midx = l3mdev_master_ifindex_rcu(dev);
8147bb387c5SDavid Ahern 
8157bb387c5SDavid Ahern 			rcu_read_unlock();
8167bb387c5SDavid Ahern 
8177bb387c5SDavid Ahern 			if (sk->sk_bound_dev_if &&
8187bb387c5SDavid Ahern 			    sk->sk_bound_dev_if != val &&
8197bb387c5SDavid Ahern 			    (!midx || midx != sk->sk_bound_dev_if))
8207bb387c5SDavid Ahern 				goto e_inval;
8214953f0fcSBrian Haley 		}
8221da177e4SLinus Torvalds 		np->mcast_oif = val;
8231da177e4SLinus Torvalds 		retv = 0;
8241da177e4SLinus Torvalds 		break;
8251da177e4SLinus Torvalds 	case IPV6_ADD_MEMBERSHIP:
8261da177e4SLinus Torvalds 	case IPV6_DROP_MEMBERSHIP:
8271da177e4SLinus Torvalds 	{
8281da177e4SLinus Torvalds 		struct ipv6_mreq mreq;
8291da177e4SLinus Torvalds 
830a28398baSWang Chen 		if (optlen < sizeof(struct ipv6_mreq))
831a28398baSWang Chen 			goto e_inval;
832a28398baSWang Chen 
833a96fb49bSFlavio Leitner 		retv = -EPROTO;
834b1c0356aSEric Dumazet 		if (inet_test_bit(IS_ICSK, sk))
835a96fb49bSFlavio Leitner 			break;
836a96fb49bSFlavio Leitner 
8371da177e4SLinus Torvalds 		retv = -EFAULT;
838894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(&mreq, optval, sizeof(struct ipv6_mreq)))
8391da177e4SLinus Torvalds 			break;
8401da177e4SLinus Torvalds 
8411da177e4SLinus Torvalds 		if (optname == IPV6_ADD_MEMBERSHIP)
84254ff9ef3SMarcelo Ricardo Leitner 			retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
8431da177e4SLinus Torvalds 		else
84454ff9ef3SMarcelo Ricardo Leitner 			retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
8451da177e4SLinus Torvalds 		break;
8461da177e4SLinus Torvalds 	}
8471da177e4SLinus Torvalds 	case IPV6_JOIN_ANYCAST:
8481da177e4SLinus Torvalds 	case IPV6_LEAVE_ANYCAST:
8491da177e4SLinus Torvalds 	{
8501da177e4SLinus Torvalds 		struct ipv6_mreq mreq;
8511da177e4SLinus Torvalds 
852a28398baSWang Chen 		if (optlen < sizeof(struct ipv6_mreq))
8531da177e4SLinus Torvalds 			goto e_inval;
8541da177e4SLinus Torvalds 
8551da177e4SLinus Torvalds 		retv = -EFAULT;
856894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(&mreq, optval, sizeof(struct ipv6_mreq)))
8571da177e4SLinus Torvalds 			break;
8581da177e4SLinus Torvalds 
8591da177e4SLinus Torvalds 		if (optname == IPV6_JOIN_ANYCAST)
8601da177e4SLinus Torvalds 			retv = ipv6_sock_ac_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
8611da177e4SLinus Torvalds 		else
8621da177e4SLinus Torvalds 			retv = ipv6_sock_ac_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
8631da177e4SLinus Torvalds 		break;
8641da177e4SLinus Torvalds 	}
86515033f04SAndre Naujoks 	case IPV6_MULTICAST_ALL:
86615033f04SAndre Naujoks 		if (optlen < sizeof(int))
86715033f04SAndre Naujoks 			goto e_inval;
86815033f04SAndre Naujoks 		np->mc_all = valbool;
86915033f04SAndre Naujoks 		retv = 0;
87015033f04SAndre Naujoks 		break;
87115033f04SAndre Naujoks 
8721da177e4SLinus Torvalds 	case MCAST_JOIN_GROUP:
8731da177e4SLinus Torvalds 	case MCAST_LEAVE_GROUP:
8743021ad52SChristoph Hellwig 		if (in_compat_syscall())
8753021ad52SChristoph Hellwig 			retv = compat_ipv6_mcast_join_leave(sk, optname, optval,
8763021ad52SChristoph Hellwig 							    optlen);
8773021ad52SChristoph Hellwig 		else
8783021ad52SChristoph Hellwig 			retv = ipv6_mcast_join_leave(sk, optname, optval,
8793021ad52SChristoph Hellwig 						     optlen);
8801da177e4SLinus Torvalds 		break;
8811da177e4SLinus Torvalds 	case MCAST_JOIN_SOURCE_GROUP:
8821da177e4SLinus Torvalds 	case MCAST_LEAVE_SOURCE_GROUP:
8831da177e4SLinus Torvalds 	case MCAST_BLOCK_SOURCE:
8841da177e4SLinus Torvalds 	case MCAST_UNBLOCK_SOURCE:
8853021ad52SChristoph Hellwig 		retv = do_ipv6_mcast_group_source(sk, optname, optval, optlen);
8861da177e4SLinus Torvalds 		break;
8871da177e4SLinus Torvalds 	case MCAST_MSFILTER:
8883021ad52SChristoph Hellwig 		if (in_compat_syscall())
8893021ad52SChristoph Hellwig 			retv = compat_ipv6_set_mcast_msfilter(sk, optval,
8903021ad52SChristoph Hellwig 							      optlen);
8913021ad52SChristoph Hellwig 		else
892ca0e65ebSChristoph Hellwig 			retv = ipv6_set_mcast_msfilter(sk, optval, optlen);
8931da177e4SLinus Torvalds 		break;
8941da177e4SLinus Torvalds 	case IPV6_ROUTER_ALERT:
895b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
896b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
897725a8ff0SDenis V. Lunev 		retv = ip6_ra_control(sk, val);
8981da177e4SLinus Torvalds 		break;
8999036b2feSFrancesco Ruggeri 	case IPV6_ROUTER_ALERT_ISOLATE:
9009036b2feSFrancesco Ruggeri 		if (optlen < sizeof(int))
9019036b2feSFrancesco Ruggeri 			goto e_inval;
9029036b2feSFrancesco Ruggeri 		np->rtalert_isolate = valbool;
9039036b2feSFrancesco Ruggeri 		retv = 0;
9049036b2feSFrancesco Ruggeri 		break;
9051da177e4SLinus Torvalds 	case IPV6_MTU_DISCOVER:
906b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
907b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
9080b95227aSHannes Frederic Sowa 		if (val < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_OMIT)
9091da177e4SLinus Torvalds 			goto e_inval;
9101da177e4SLinus Torvalds 		np->pmtudisc = val;
9111da177e4SLinus Torvalds 		retv = 0;
9121da177e4SLinus Torvalds 		break;
9131da177e4SLinus Torvalds 	case IPV6_MTU:
914b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
915b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
9161da177e4SLinus Torvalds 		if (val && val < IPV6_MIN_MTU)
9171da177e4SLinus Torvalds 			goto e_inval;
9181da177e4SLinus Torvalds 		np->frag_size = val;
9191da177e4SLinus Torvalds 		retv = 0;
9201da177e4SLinus Torvalds 		break;
9211da177e4SLinus Torvalds 	case IPV6_RECVERR:
922b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
923b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
9241da177e4SLinus Torvalds 		np->recverr = valbool;
9251da177e4SLinus Torvalds 		if (!val)
926*0f158b32SEric Dumazet 			skb_errqueue_purge(&sk->sk_error_queue);
9271da177e4SLinus Torvalds 		retv = 0;
9281da177e4SLinus Torvalds 		break;
9291da177e4SLinus Torvalds 	case IPV6_FLOWINFO_SEND:
930b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
931b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
9321da177e4SLinus Torvalds 		np->sndflow = valbool;
9331da177e4SLinus Torvalds 		retv = 0;
9341da177e4SLinus Torvalds 		break;
9351da177e4SLinus Torvalds 	case IPV6_FLOWLABEL_MGR:
936894cfbc0SChristoph Hellwig 		retv = ipv6_flowlabel_opt(sk, optval, optlen);
9371da177e4SLinus Torvalds 		break;
9381da177e4SLinus Torvalds 	case IPV6_IPSEC_POLICY:
9391da177e4SLinus Torvalds 	case IPV6_XFRM_POLICY:
9406fc0b4a7SHerbert Xu 		retv = -EPERM;
94140cd308eSMartin KaFai Lau 		if (!sockopt_ns_capable(net->user_ns, CAP_NET_ADMIN))
9426fc0b4a7SHerbert Xu 			break;
943894cfbc0SChristoph Hellwig 		retv = xfrm_user_policy(sk, optname, optval, optlen);
9441da177e4SLinus Torvalds 		break;
9451da177e4SLinus Torvalds 
9467cbca67cSYOSHIFUJI Hideaki 	case IPV6_ADDR_PREFERENCES:
947b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
948b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
94918d5ad62SChristoph Hellwig 		retv = __ip6_sock_set_addr_preferences(sk, val);
9507cbca67cSYOSHIFUJI Hideaki 		break;
951e802af9cSStephen Hemminger 	case IPV6_MINHOPCOUNT:
952e802af9cSStephen Hemminger 		if (optlen < sizeof(int))
953e802af9cSStephen Hemminger 			goto e_inval;
954e802af9cSStephen Hemminger 		if (val < 0 || val > 255)
955e802af9cSStephen Hemminger 			goto e_inval;
956790eb673SEric Dumazet 
957790eb673SEric Dumazet 		if (val)
958790eb673SEric Dumazet 			static_branch_enable(&ip6_min_hopcount);
959790eb673SEric Dumazet 
960cc17c3c8SEric Dumazet 		/* tcp_v6_err() and tcp_v6_rcv() might read min_hopcount
961cc17c3c8SEric Dumazet 		 * while we are changing it.
962cc17c3c8SEric Dumazet 		 */
963cc17c3c8SEric Dumazet 		WRITE_ONCE(np->min_hopcount, val);
964d4596badSHannes Frederic Sowa 		retv = 0;
965793b1473SBrian Haley 		break;
966793b1473SBrian Haley 	case IPV6_DONTFRAG:
967793b1473SBrian Haley 		np->dontfrag = valbool;
968e802af9cSStephen Hemminger 		retv = 0;
969e802af9cSStephen Hemminger 		break;
970cb1ce2efSTom Herbert 	case IPV6_AUTOFLOWLABEL:
971cb1ce2efSTom Herbert 		np->autoflowlabel = valbool;
972513674b5SShaohua Li 		np->autoflowlabel_set = 1;
973cb1ce2efSTom Herbert 		retv = 0;
974cb1ce2efSTom Herbert 		break;
9750cc0aa61SWillem de Bruijn 	case IPV6_RECVFRAGSIZE:
9760cc0aa61SWillem de Bruijn 		np->rxopt.bits.recvfragsize = valbool;
9770cc0aa61SWillem de Bruijn 		retv = 0;
9780cc0aa61SWillem de Bruijn 		break;
97901370434SWillem de Bruijn 	case IPV6_RECVERR_RFC4884:
98001370434SWillem de Bruijn 		if (optlen < sizeof(int))
98101370434SWillem de Bruijn 			goto e_inval;
98201370434SWillem de Bruijn 		if (val < 0 || val > 1)
98301370434SWillem de Bruijn 			goto e_inval;
98401370434SWillem de Bruijn 		np->recverr_rfc4884 = valbool;
98501370434SWillem de Bruijn 		retv = 0;
98601370434SWillem de Bruijn 		break;
9877cbca67cSYOSHIFUJI Hideaki 	}
9887cbca67cSYOSHIFUJI Hideaki 
9893c52c6bbSKuniyuki Iwashima unlock:
99040cd308eSMartin KaFai Lau 	sockopt_release_sock(sk);
991baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
992baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
9931da177e4SLinus Torvalds 
9941da177e4SLinus Torvalds 	return retv;
9951da177e4SLinus Torvalds 
9961da177e4SLinus Torvalds e_inval:
997b45a337fSKuniyuki Iwashima 	retv = -EINVAL;
998b45a337fSKuniyuki Iwashima 	goto unlock;
9991da177e4SLinus Torvalds }
10001da177e4SLinus Torvalds 
ipv6_setsockopt(struct sock * sk,int level,int optname,sockptr_t optval,unsigned int optlen)1001a7b75c5aSChristoph Hellwig int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
1002a7b75c5aSChristoph Hellwig 		    unsigned int optlen)
10033fdadf7dSDmitry Mishin {
10043fdadf7dSDmitry Mishin 	int err;
10053fdadf7dSDmitry Mishin 
10063fdadf7dSDmitry Mishin 	if (level == SOL_IP && sk->sk_type != SOCK_RAW)
10073fdadf7dSDmitry Mishin 		return udp_prot.setsockopt(sk, level, optname, optval, optlen);
10083fdadf7dSDmitry Mishin 
10093fdadf7dSDmitry Mishin 	if (level != SOL_IPV6)
10103fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
10113fdadf7dSDmitry Mishin 
1012a7b75c5aSChristoph Hellwig 	err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
10133fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
10143fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
10153fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IPV6_IPSEC_POLICY &&
10163f34cfaeSPaolo Abeni 			optname != IPV6_XFRM_POLICY)
1017a7b75c5aSChristoph Hellwig 		err = nf_setsockopt(sk, PF_INET6, optname, optval, optlen);
10183fdadf7dSDmitry Mishin #endif
10193fdadf7dSDmitry Mishin 	return err;
10203fdadf7dSDmitry Mishin }
10217159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ipv6_setsockopt);
10223fdadf7dSDmitry Mishin 
ipv6_getsockopt_sticky(struct sock * sk,struct ipv6_txoptions * opt,int optname,sockptr_t optval,int len)102328693079SDavid S. Miller static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
10246dadbe4bSMartin KaFai Lau 				  int optname, sockptr_t optval, int len)
1025333fad53SYOSHIFUJI Hideaki {
102628693079SDavid S. Miller 	struct ipv6_opt_hdr *hdr;
102728693079SDavid S. Miller 
10284c6510a7SYOSHIFUJI Hideaki 	if (!opt)
1029333fad53SYOSHIFUJI Hideaki 		return 0;
10304c6510a7SYOSHIFUJI Hideaki 
10314c6510a7SYOSHIFUJI Hideaki 	switch (optname) {
10324c6510a7SYOSHIFUJI Hideaki 	case IPV6_HOPOPTS:
103328693079SDavid S. Miller 		hdr = opt->hopopt;
10344c6510a7SYOSHIFUJI Hideaki 		break;
10354c6510a7SYOSHIFUJI Hideaki 	case IPV6_RTHDRDSTOPTS:
10364c6510a7SYOSHIFUJI Hideaki 		hdr = opt->dst0opt;
10374c6510a7SYOSHIFUJI Hideaki 		break;
10384c6510a7SYOSHIFUJI Hideaki 	case IPV6_RTHDR:
10394c6510a7SYOSHIFUJI Hideaki 		hdr = (struct ipv6_opt_hdr *)opt->srcrt;
10404c6510a7SYOSHIFUJI Hideaki 		break;
10414c6510a7SYOSHIFUJI Hideaki 	case IPV6_DSTOPTS:
10424c6510a7SYOSHIFUJI Hideaki 		hdr = opt->dst1opt;
10434c6510a7SYOSHIFUJI Hideaki 		break;
10444c6510a7SYOSHIFUJI Hideaki 	default:
10454c6510a7SYOSHIFUJI Hideaki 		return -EINVAL;	/* should not happen */
10464c6510a7SYOSHIFUJI Hideaki 	}
10474c6510a7SYOSHIFUJI Hideaki 
10484c6510a7SYOSHIFUJI Hideaki 	if (!hdr)
10494c6510a7SYOSHIFUJI Hideaki 		return 0;
105028693079SDavid S. Miller 
1051d2b02ed9SChris Wright 	len = min_t(unsigned int, len, ipv6_optlen(hdr));
10526dadbe4bSMartin KaFai Lau 	if (copy_to_sockptr(optval, hdr, len))
1053333fad53SYOSHIFUJI Hideaki 		return -EFAULT;
105495b496b6SYang Hongyang 	return len;
1055333fad53SYOSHIFUJI Hideaki }
1056333fad53SYOSHIFUJI Hideaki 
ipv6_get_msfilter(struct sock * sk,sockptr_t optval,sockptr_t optlen,int len)10576dadbe4bSMartin KaFai Lau static int ipv6_get_msfilter(struct sock *sk, sockptr_t optval,
10586dadbe4bSMartin KaFai Lau 			     sockptr_t optlen, int len)
10591da177e4SLinus Torvalds {
1060db243b79SGustavo A. R. Silva 	const int size0 = offsetof(struct group_filter, gf_slist_flex);
1061d5541e85SChristoph Hellwig 	struct group_filter gsf;
1062931ca7abSAl Viro 	int num;
10631da177e4SLinus Torvalds 	int err;
10641da177e4SLinus Torvalds 
1065931ca7abSAl Viro 	if (len < size0)
10661da177e4SLinus Torvalds 		return -EINVAL;
10676dadbe4bSMartin KaFai Lau 	if (copy_from_sockptr(&gsf, optval, size0))
10681da177e4SLinus Torvalds 		return -EFAULT;
106920c61fbdSYOSHIFUJI Hideaki 	if (gsf.gf_group.ss_family != AF_INET6)
107020c61fbdSYOSHIFUJI Hideaki 		return -EADDRNOTAVAIL;
1071931ca7abSAl Viro 	num = gsf.gf_numsrc;
10720f95f7d4SMartin KaFai Lau 	sockopt_lock_sock(sk);
10736dadbe4bSMartin KaFai Lau 	err = ip6_mc_msfget(sk, &gsf, optval, size0);
1074931ca7abSAl Viro 	if (!err) {
1075931ca7abSAl Viro 		if (num > gsf.gf_numsrc)
1076931ca7abSAl Viro 			num = gsf.gf_numsrc;
10776dadbe4bSMartin KaFai Lau 		len = GROUP_FILTER_SIZE(num);
10786dadbe4bSMartin KaFai Lau 		if (copy_to_sockptr(optlen, &len, sizeof(int)) ||
10796dadbe4bSMartin KaFai Lau 		    copy_to_sockptr(optval, &gsf, size0))
1080931ca7abSAl Viro 			err = -EFAULT;
1081931ca7abSAl Viro 	}
10820f95f7d4SMartin KaFai Lau 	sockopt_release_sock(sk);
10831da177e4SLinus Torvalds 	return err;
10841da177e4SLinus Torvalds }
10851da177e4SLinus Torvalds 
compat_ipv6_get_msfilter(struct sock * sk,sockptr_t optval,sockptr_t optlen,int len)10866dadbe4bSMartin KaFai Lau static int compat_ipv6_get_msfilter(struct sock *sk, sockptr_t optval,
10876dadbe4bSMartin KaFai Lau 				    sockptr_t optlen, int len)
1088d5541e85SChristoph Hellwig {
1089db243b79SGustavo A. R. Silva 	const int size0 = offsetof(struct compat_group_filter, gf_slist_flex);
1090d5541e85SChristoph Hellwig 	struct compat_group_filter gf32;
1091d5541e85SChristoph Hellwig 	struct group_filter gf;
10929c3f9707SMartin KaFai Lau 	int err;
1093d5541e85SChristoph Hellwig 	int num;
1094d5541e85SChristoph Hellwig 
1095d5541e85SChristoph Hellwig 	if (len < size0)
1096d5541e85SChristoph Hellwig 		return -EINVAL;
1097d5541e85SChristoph Hellwig 
10986dadbe4bSMartin KaFai Lau 	if (copy_from_sockptr(&gf32, optval, size0))
1099d5541e85SChristoph Hellwig 		return -EFAULT;
1100d5541e85SChristoph Hellwig 	gf.gf_interface = gf32.gf_interface;
1101d5541e85SChristoph Hellwig 	gf.gf_fmode = gf32.gf_fmode;
1102d5541e85SChristoph Hellwig 	num = gf.gf_numsrc = gf32.gf_numsrc;
1103d5541e85SChristoph Hellwig 	gf.gf_group = gf32.gf_group;
1104d5541e85SChristoph Hellwig 
1105d5541e85SChristoph Hellwig 	if (gf.gf_group.ss_family != AF_INET6)
1106d5541e85SChristoph Hellwig 		return -EADDRNOTAVAIL;
1107d5541e85SChristoph Hellwig 
11080f95f7d4SMartin KaFai Lau 	sockopt_lock_sock(sk);
11096dadbe4bSMartin KaFai Lau 	err = ip6_mc_msfget(sk, &gf, optval, size0);
11100f95f7d4SMartin KaFai Lau 	sockopt_release_sock(sk);
1111d5541e85SChristoph Hellwig 	if (err)
1112d5541e85SChristoph Hellwig 		return err;
1113d5541e85SChristoph Hellwig 	if (num > gf.gf_numsrc)
1114d5541e85SChristoph Hellwig 		num = gf.gf_numsrc;
1115d5541e85SChristoph Hellwig 	len = GROUP_FILTER_SIZE(num) - (sizeof(gf)-sizeof(gf32));
11166dadbe4bSMartin KaFai Lau 	if (copy_to_sockptr(optlen, &len, sizeof(int)) ||
11176dadbe4bSMartin KaFai Lau 	    copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_fmode),
11186dadbe4bSMartin KaFai Lau 				   &gf.gf_fmode, sizeof(gf32.gf_fmode)) ||
11196dadbe4bSMartin KaFai Lau 	    copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_numsrc),
11206dadbe4bSMartin KaFai Lau 				   &gf.gf_numsrc, sizeof(gf32.gf_numsrc)))
1121d5541e85SChristoph Hellwig 		return -EFAULT;
1122d5541e85SChristoph Hellwig 	return 0;
1123d5541e85SChristoph Hellwig }
1124d5541e85SChristoph Hellwig 
do_ipv6_getsockopt(struct sock * sk,int level,int optname,sockptr_t optval,sockptr_t optlen)112538566ec0SMartin KaFai Lau int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
11266dadbe4bSMartin KaFai Lau 		       sockptr_t optval, sockptr_t optlen)
1127d5541e85SChristoph Hellwig {
1128d5541e85SChristoph Hellwig 	struct ipv6_pinfo *np = inet6_sk(sk);
1129d5541e85SChristoph Hellwig 	int len;
1130d5541e85SChristoph Hellwig 	int val;
1131d5541e85SChristoph Hellwig 
1132d5541e85SChristoph Hellwig 	if (ip6_mroute_opt(optname))
1133d5541e85SChristoph Hellwig 		return ip6_mroute_getsockopt(sk, optname, optval, optlen);
1134d5541e85SChristoph Hellwig 
11356dadbe4bSMartin KaFai Lau 	if (copy_from_sockptr(&len, optlen, sizeof(int)))
1136d5541e85SChristoph Hellwig 		return -EFAULT;
1137d5541e85SChristoph Hellwig 	switch (optname) {
1138d5541e85SChristoph Hellwig 	case IPV6_ADDRFORM:
1139d5541e85SChristoph Hellwig 		if (sk->sk_protocol != IPPROTO_UDP &&
1140d5541e85SChristoph Hellwig 		    sk->sk_protocol != IPPROTO_UDPLITE &&
1141d5541e85SChristoph Hellwig 		    sk->sk_protocol != IPPROTO_TCP)
1142d5541e85SChristoph Hellwig 			return -ENOPROTOOPT;
1143d5541e85SChristoph Hellwig 		if (sk->sk_state != TCP_ESTABLISHED)
1144d5541e85SChristoph Hellwig 			return -ENOTCONN;
1145d5541e85SChristoph Hellwig 		val = sk->sk_family;
1146d5541e85SChristoph Hellwig 		break;
1147d5541e85SChristoph Hellwig 	case MCAST_MSFILTER:
11483021ad52SChristoph Hellwig 		if (in_compat_syscall())
11499c3f9707SMartin KaFai Lau 			return compat_ipv6_get_msfilter(sk, optval, optlen, len);
1150d5541e85SChristoph Hellwig 		return ipv6_get_msfilter(sk, optval, optlen, len);
1151333fad53SYOSHIFUJI Hideaki 	case IPV6_2292PKTOPTIONS:
11521da177e4SLinus Torvalds 	{
11531da177e4SLinus Torvalds 		struct msghdr msg;
11541da177e4SLinus Torvalds 		struct sk_buff *skb;
11551da177e4SLinus Torvalds 
11561da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_STREAM)
11571da177e4SLinus Torvalds 			return -ENOPROTOOPT;
11581da177e4SLinus Torvalds 
11596dadbe4bSMartin KaFai Lau 		if (optval.is_kernel) {
11606dadbe4bSMartin KaFai Lau 			msg.msg_control_is_user = false;
11616dadbe4bSMartin KaFai Lau 			msg.msg_control = optval.kernel;
11626dadbe4bSMartin KaFai Lau 		} else {
11631b2f08dfSChristoph Hellwig 			msg.msg_control_is_user = true;
11646dadbe4bSMartin KaFai Lau 			msg.msg_control_user = optval.user;
11656dadbe4bSMartin KaFai Lau 		}
11661da177e4SLinus Torvalds 		msg.msg_controllen = len;
116775f23979SMartin KaFai Lau 		msg.msg_flags = 0;
11681da177e4SLinus Torvalds 
11690f95f7d4SMartin KaFai Lau 		sockopt_lock_sock(sk);
11701da177e4SLinus Torvalds 		skb = np->pktoptions;
11711da177e4SLinus Torvalds 		if (skb)
11724b261c75SHannes Frederic Sowa 			ip6_datagram_recv_ctl(sk, &msg, skb);
11730f95f7d4SMartin KaFai Lau 		sockopt_release_sock(sk);
11741dc7b90fSEric Dumazet 		if (!skb) {
11751da177e4SLinus Torvalds 			if (np->rxopt.bits.rxinfo) {
11761da177e4SLinus Torvalds 				struct in6_pktinfo src_info;
1177f250dcdaSYang Hongyang 				src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
1178f250dcdaSYang Hongyang 					np->sticky_pktinfo.ipi6_ifindex;
1179efe4208fSEric Dumazet 				src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr : np->sticky_pktinfo.ipi6_addr;
11801da177e4SLinus Torvalds 				put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
11811da177e4SLinus Torvalds 			}
11821da177e4SLinus Torvalds 			if (np->rxopt.bits.rxhlim) {
11831da177e4SLinus Torvalds 				int hlim = np->mcast_hops;
11841da177e4SLinus Torvalds 				put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
11851da177e4SLinus Torvalds 			}
11864c507d28SJiri Benc 			if (np->rxopt.bits.rxtclass) {
1187d76ed22bSLi RongQing 				int tclass = (int)ip6_tclass(np->rcv_flowinfo);
1188d76ed22bSLi RongQing 
11894c507d28SJiri Benc 				put_cmsg(&msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
11904c507d28SJiri Benc 			}
1191333fad53SYOSHIFUJI Hideaki 			if (np->rxopt.bits.rxoinfo) {
1192333fad53SYOSHIFUJI Hideaki 				struct in6_pktinfo src_info;
1193f250dcdaSYang Hongyang 				src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
1194f250dcdaSYang Hongyang 					np->sticky_pktinfo.ipi6_ifindex;
1195efe4208fSEric Dumazet 				src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr :
1196efe4208fSEric Dumazet 								     np->sticky_pktinfo.ipi6_addr;
1197333fad53SYOSHIFUJI Hideaki 				put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
1198333fad53SYOSHIFUJI Hideaki 			}
1199333fad53SYOSHIFUJI Hideaki 			if (np->rxopt.bits.rxohlim) {
1200333fad53SYOSHIFUJI Hideaki 				int hlim = np->mcast_hops;
1201333fad53SYOSHIFUJI Hideaki 				put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
1202333fad53SYOSHIFUJI Hideaki 			}
12031397ed35SFlorent Fourcot 			if (np->rxopt.bits.rxflow) {
120468536053SFlorent Fourcot 				__be32 flowinfo = np->rcv_flowinfo;
120568536053SFlorent Fourcot 
12061397ed35SFlorent Fourcot 				put_cmsg(&msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
12071397ed35SFlorent Fourcot 			}
12081da177e4SLinus Torvalds 		}
12091da177e4SLinus Torvalds 		len -= msg.msg_controllen;
12106dadbe4bSMartin KaFai Lau 		return copy_to_sockptr(optlen, &len, sizeof(int));
12111da177e4SLinus Torvalds 	}
12121da177e4SLinus Torvalds 	case IPV6_MTU:
12131da177e4SLinus Torvalds 	{
12141da177e4SLinus Torvalds 		struct dst_entry *dst;
1215b6c6712aSEric Dumazet 
12161da177e4SLinus Torvalds 		val = 0;
1217b6c6712aSEric Dumazet 		rcu_read_lock();
1218b6c6712aSEric Dumazet 		dst = __sk_dst_get(sk);
1219b6c6712aSEric Dumazet 		if (dst)
12201da177e4SLinus Torvalds 			val = dst_mtu(dst);
1221b6c6712aSEric Dumazet 		rcu_read_unlock();
12221da177e4SLinus Torvalds 		if (!val)
12231da177e4SLinus Torvalds 			return -ENOTCONN;
12241da177e4SLinus Torvalds 		break;
12251da177e4SLinus Torvalds 	}
12261da177e4SLinus Torvalds 
12271da177e4SLinus Torvalds 	case IPV6_V6ONLY:
12289fe516baSEric Dumazet 		val = sk->sk_ipv6only;
12291da177e4SLinus Torvalds 		break;
12301da177e4SLinus Torvalds 
1231333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVPKTINFO:
12321da177e4SLinus Torvalds 		val = np->rxopt.bits.rxinfo;
12331da177e4SLinus Torvalds 		break;
12341da177e4SLinus Torvalds 
1235333fad53SYOSHIFUJI Hideaki 	case IPV6_2292PKTINFO:
1236333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.rxoinfo;
1237333fad53SYOSHIFUJI Hideaki 		break;
1238333fad53SYOSHIFUJI Hideaki 
1239333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVHOPLIMIT:
12401da177e4SLinus Torvalds 		val = np->rxopt.bits.rxhlim;
12411da177e4SLinus Torvalds 		break;
12421da177e4SLinus Torvalds 
1243333fad53SYOSHIFUJI Hideaki 	case IPV6_2292HOPLIMIT:
1244333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.rxohlim;
1245333fad53SYOSHIFUJI Hideaki 		break;
1246333fad53SYOSHIFUJI Hideaki 
1247333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVRTHDR:
12481da177e4SLinus Torvalds 		val = np->rxopt.bits.srcrt;
12491da177e4SLinus Torvalds 		break;
12501da177e4SLinus Torvalds 
1251333fad53SYOSHIFUJI Hideaki 	case IPV6_2292RTHDR:
1252333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.osrcrt;
1253333fad53SYOSHIFUJI Hideaki 		break;
1254333fad53SYOSHIFUJI Hideaki 
12551da177e4SLinus Torvalds 	case IPV6_HOPOPTS:
1256333fad53SYOSHIFUJI Hideaki 	case IPV6_RTHDRDSTOPTS:
1257333fad53SYOSHIFUJI Hideaki 	case IPV6_RTHDR:
1258333fad53SYOSHIFUJI Hideaki 	case IPV6_DSTOPTS:
1259333fad53SYOSHIFUJI Hideaki 	{
126045f6fad8SEric Dumazet 		struct ipv6_txoptions *opt;
1261333fad53SYOSHIFUJI Hideaki 
12620f95f7d4SMartin KaFai Lau 		sockopt_lock_sock(sk);
12631e1d04e6SHannes Frederic Sowa 		opt = rcu_dereference_protected(np->opt,
12641e1d04e6SHannes Frederic Sowa 						lockdep_sock_is_held(sk));
126545f6fad8SEric Dumazet 		len = ipv6_getsockopt_sticky(sk, opt, optname, optval, len);
12660f95f7d4SMartin KaFai Lau 		sockopt_release_sock(sk);
126705335c22SYang Hongyang 		/* check if ipv6_getsockopt_sticky() returns err code */
126805335c22SYang Hongyang 		if (len < 0)
126905335c22SYang Hongyang 			return len;
12706dadbe4bSMartin KaFai Lau 		return copy_to_sockptr(optlen, &len, sizeof(int));
1271333fad53SYOSHIFUJI Hideaki 	}
1272333fad53SYOSHIFUJI Hideaki 
1273333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVHOPOPTS:
12741da177e4SLinus Torvalds 		val = np->rxopt.bits.hopopts;
12751da177e4SLinus Torvalds 		break;
12761da177e4SLinus Torvalds 
1277333fad53SYOSHIFUJI Hideaki 	case IPV6_2292HOPOPTS:
1278333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.ohopopts;
1279333fad53SYOSHIFUJI Hideaki 		break;
1280333fad53SYOSHIFUJI Hideaki 
1281333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVDSTOPTS:
12821da177e4SLinus Torvalds 		val = np->rxopt.bits.dstopts;
12831da177e4SLinus Torvalds 		break;
12841da177e4SLinus Torvalds 
1285333fad53SYOSHIFUJI Hideaki 	case IPV6_2292DSTOPTS:
1286333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.odstopts;
1287333fad53SYOSHIFUJI Hideaki 		break;
1288333fad53SYOSHIFUJI Hideaki 
128941a1f8eaSYOSHIFUJI Hideaki 	case IPV6_TCLASS:
129041a1f8eaSYOSHIFUJI Hideaki 		val = np->tclass;
129141a1f8eaSYOSHIFUJI Hideaki 		break;
129241a1f8eaSYOSHIFUJI Hideaki 
129341a1f8eaSYOSHIFUJI Hideaki 	case IPV6_RECVTCLASS:
129441a1f8eaSYOSHIFUJI Hideaki 		val = np->rxopt.bits.rxtclass;
129541a1f8eaSYOSHIFUJI Hideaki 		break;
129641a1f8eaSYOSHIFUJI Hideaki 
12971da177e4SLinus Torvalds 	case IPV6_FLOWINFO:
12981da177e4SLinus Torvalds 		val = np->rxopt.bits.rxflow;
12991da177e4SLinus Torvalds 		break;
13001da177e4SLinus Torvalds 
1301793b1473SBrian Haley 	case IPV6_RECVPATHMTU:
1302793b1473SBrian Haley 		val = np->rxopt.bits.rxpmtu;
1303793b1473SBrian Haley 		break;
1304793b1473SBrian Haley 
1305793b1473SBrian Haley 	case IPV6_PATHMTU:
1306793b1473SBrian Haley 	{
1307793b1473SBrian Haley 		struct dst_entry *dst;
1308793b1473SBrian Haley 		struct ip6_mtuinfo mtuinfo;
1309793b1473SBrian Haley 
1310793b1473SBrian Haley 		if (len < sizeof(mtuinfo))
1311793b1473SBrian Haley 			return -EINVAL;
1312793b1473SBrian Haley 
1313793b1473SBrian Haley 		len = sizeof(mtuinfo);
1314793b1473SBrian Haley 		memset(&mtuinfo, 0, sizeof(mtuinfo));
1315793b1473SBrian Haley 
1316793b1473SBrian Haley 		rcu_read_lock();
1317793b1473SBrian Haley 		dst = __sk_dst_get(sk);
1318793b1473SBrian Haley 		if (dst)
1319793b1473SBrian Haley 			mtuinfo.ip6m_mtu = dst_mtu(dst);
1320793b1473SBrian Haley 		rcu_read_unlock();
1321793b1473SBrian Haley 		if (!mtuinfo.ip6m_mtu)
1322793b1473SBrian Haley 			return -ENOTCONN;
1323793b1473SBrian Haley 
13246dadbe4bSMartin KaFai Lau 		if (copy_to_sockptr(optlen, &len, sizeof(int)))
1325793b1473SBrian Haley 			return -EFAULT;
13266dadbe4bSMartin KaFai Lau 		if (copy_to_sockptr(optval, &mtuinfo, len))
1327793b1473SBrian Haley 			return -EFAULT;
1328793b1473SBrian Haley 
1329793b1473SBrian Haley 		return 0;
1330793b1473SBrian Haley 	}
1331793b1473SBrian Haley 
13326c468622SBalazs Scheidler 	case IPV6_TRANSPARENT:
13334bd0623fSEric Dumazet 		val = inet_test_bit(TRANSPARENT, sk);
13346c468622SBalazs Scheidler 		break;
13356c468622SBalazs Scheidler 
133684e14fe3SMaciej Żenczykowski 	case IPV6_FREEBIND:
13373f7e7532SEric Dumazet 		val = inet_test_bit(FREEBIND, sk);
133884e14fe3SMaciej Żenczykowski 		break;
133984e14fe3SMaciej Żenczykowski 
13406c468622SBalazs Scheidler 	case IPV6_RECVORIGDSTADDR:
13416c468622SBalazs Scheidler 		val = np->rxopt.bits.rxorigdstaddr;
13426c468622SBalazs Scheidler 		break;
13436c468622SBalazs Scheidler 
13441da177e4SLinus Torvalds 	case IPV6_UNICAST_HOPS:
13451da177e4SLinus Torvalds 	case IPV6_MULTICAST_HOPS:
1346befffe90SBrian Haley 	{
1347befffe90SBrian Haley 		struct dst_entry *dst;
1348befffe90SBrian Haley 
1349befffe90SBrian Haley 		if (optname == IPV6_UNICAST_HOPS)
1350befffe90SBrian Haley 			val = np->hop_limit;
1351befffe90SBrian Haley 		else
13521da177e4SLinus Torvalds 			val = np->mcast_hops;
1353befffe90SBrian Haley 
1354b6c6712aSEric Dumazet 		if (val < 0) {
1355b6c6712aSEric Dumazet 			rcu_read_lock();
1356b6c6712aSEric Dumazet 			dst = __sk_dst_get(sk);
1357b6c6712aSEric Dumazet 			if (dst)
13586b75d090SYOSHIFUJI Hideaki 				val = ip6_dst_hoplimit(dst);
1359b6c6712aSEric Dumazet 			rcu_read_unlock();
1360befffe90SBrian Haley 		}
1361b6c6712aSEric Dumazet 
1362befffe90SBrian Haley 		if (val < 0)
136353b7997fSYOSHIFUJI Hideaki 			val = sock_net(sk)->ipv6.devconf_all->hop_limit;
13641da177e4SLinus Torvalds 		break;
1365befffe90SBrian Haley 	}
13661da177e4SLinus Torvalds 
13671da177e4SLinus Torvalds 	case IPV6_MULTICAST_LOOP:
13681da177e4SLinus Torvalds 		val = np->mc_loop;
13691da177e4SLinus Torvalds 		break;
13701da177e4SLinus Torvalds 
13711da177e4SLinus Torvalds 	case IPV6_MULTICAST_IF:
13721da177e4SLinus Torvalds 		val = np->mcast_oif;
13731da177e4SLinus Torvalds 		break;
13741da177e4SLinus Torvalds 
137515033f04SAndre Naujoks 	case IPV6_MULTICAST_ALL:
137615033f04SAndre Naujoks 		val = np->mc_all;
137715033f04SAndre Naujoks 		break;
137815033f04SAndre Naujoks 
1379c4062dfcSErich E. Hoover 	case IPV6_UNICAST_IF:
1380c4062dfcSErich E. Hoover 		val = (__force int)htonl((__u32) np->ucast_oif);
1381c4062dfcSErich E. Hoover 		break;
1382c4062dfcSErich E. Hoover 
13831da177e4SLinus Torvalds 	case IPV6_MTU_DISCOVER:
13841da177e4SLinus Torvalds 		val = np->pmtudisc;
13851da177e4SLinus Torvalds 		break;
13861da177e4SLinus Torvalds 
13871da177e4SLinus Torvalds 	case IPV6_RECVERR:
13881da177e4SLinus Torvalds 		val = np->recverr;
13891da177e4SLinus Torvalds 		break;
13901da177e4SLinus Torvalds 
13911da177e4SLinus Torvalds 	case IPV6_FLOWINFO_SEND:
13921da177e4SLinus Torvalds 		val = np->sndflow;
13931da177e4SLinus Torvalds 		break;
13941da177e4SLinus Torvalds 
13953fdfa5ffSFlorent Fourcot 	case IPV6_FLOWLABEL_MGR:
13963fdfa5ffSFlorent Fourcot 	{
13973fdfa5ffSFlorent Fourcot 		struct in6_flowlabel_req freq;
139846e5f401SFlorent Fourcot 		int flags;
13993fdfa5ffSFlorent Fourcot 
14003fdfa5ffSFlorent Fourcot 		if (len < sizeof(freq))
14013fdfa5ffSFlorent Fourcot 			return -EINVAL;
14023fdfa5ffSFlorent Fourcot 
14036dadbe4bSMartin KaFai Lau 		if (copy_from_sockptr(&freq, optval, sizeof(freq)))
14043fdfa5ffSFlorent Fourcot 			return -EFAULT;
14053fdfa5ffSFlorent Fourcot 
14063fdfa5ffSFlorent Fourcot 		if (freq.flr_action != IPV6_FL_A_GET)
14073fdfa5ffSFlorent Fourcot 			return -EINVAL;
14083fdfa5ffSFlorent Fourcot 
14093fdfa5ffSFlorent Fourcot 		len = sizeof(freq);
141046e5f401SFlorent Fourcot 		flags = freq.flr_flags;
141146e5f401SFlorent Fourcot 
14123fdfa5ffSFlorent Fourcot 		memset(&freq, 0, sizeof(freq));
14133fdfa5ffSFlorent Fourcot 
141446e5f401SFlorent Fourcot 		val = ipv6_flowlabel_opt_get(sk, &freq, flags);
14153fdfa5ffSFlorent Fourcot 		if (val < 0)
14163fdfa5ffSFlorent Fourcot 			return val;
14173fdfa5ffSFlorent Fourcot 
14186dadbe4bSMartin KaFai Lau 		if (copy_to_sockptr(optlen, &len, sizeof(int)))
14193fdfa5ffSFlorent Fourcot 			return -EFAULT;
14206dadbe4bSMartin KaFai Lau 		if (copy_to_sockptr(optval, &freq, len))
14213fdfa5ffSFlorent Fourcot 			return -EFAULT;
14223fdfa5ffSFlorent Fourcot 
14233fdfa5ffSFlorent Fourcot 		return 0;
14243fdfa5ffSFlorent Fourcot 	}
14253fdfa5ffSFlorent Fourcot 
14267cbca67cSYOSHIFUJI Hideaki 	case IPV6_ADDR_PREFERENCES:
14277cbca67cSYOSHIFUJI Hideaki 		val = 0;
14287cbca67cSYOSHIFUJI Hideaki 
14297cbca67cSYOSHIFUJI Hideaki 		if (np->srcprefs & IPV6_PREFER_SRC_TMP)
14307cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_TMP;
14317cbca67cSYOSHIFUJI Hideaki 		else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC)
14327cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_PUBLIC;
14337cbca67cSYOSHIFUJI Hideaki 		else {
14347cbca67cSYOSHIFUJI Hideaki 			/* XXX: should we return system default? */
14357cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT;
14367cbca67cSYOSHIFUJI Hideaki 		}
14377cbca67cSYOSHIFUJI Hideaki 
14387cbca67cSYOSHIFUJI Hideaki 		if (np->srcprefs & IPV6_PREFER_SRC_COA)
14397cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_COA;
14407cbca67cSYOSHIFUJI Hideaki 		else
14417cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_HOME;
14427cbca67cSYOSHIFUJI Hideaki 		break;
14437cbca67cSYOSHIFUJI Hideaki 
1444e802af9cSStephen Hemminger 	case IPV6_MINHOPCOUNT:
1445e802af9cSStephen Hemminger 		val = np->min_hopcount;
1446e802af9cSStephen Hemminger 		break;
1447e802af9cSStephen Hemminger 
1448793b1473SBrian Haley 	case IPV6_DONTFRAG:
1449793b1473SBrian Haley 		val = np->dontfrag;
1450793b1473SBrian Haley 		break;
1451793b1473SBrian Haley 
1452cb1ce2efSTom Herbert 	case IPV6_AUTOFLOWLABEL:
1453e9191ffbSBen Hutchings 		val = ip6_autoflowlabel(sock_net(sk), np);
1454cb1ce2efSTom Herbert 		break;
1455cb1ce2efSTom Herbert 
14560cc0aa61SWillem de Bruijn 	case IPV6_RECVFRAGSIZE:
14570cc0aa61SWillem de Bruijn 		val = np->rxopt.bits.recvfragsize;
14580cc0aa61SWillem de Bruijn 		break;
14590cc0aa61SWillem de Bruijn 
14609036b2feSFrancesco Ruggeri 	case IPV6_ROUTER_ALERT_ISOLATE:
14619036b2feSFrancesco Ruggeri 		val = np->rtalert_isolate;
14629036b2feSFrancesco Ruggeri 		break;
14639036b2feSFrancesco Ruggeri 
146401370434SWillem de Bruijn 	case IPV6_RECVERR_RFC4884:
146501370434SWillem de Bruijn 		val = np->recverr_rfc4884;
146601370434SWillem de Bruijn 		break;
146701370434SWillem de Bruijn 
14681da177e4SLinus Torvalds 	default:
1469cf6fc4a9SWei Yongjun 		return -ENOPROTOOPT;
14701da177e4SLinus Torvalds 	}
14711da177e4SLinus Torvalds 	len = min_t(unsigned int, sizeof(int), len);
14726dadbe4bSMartin KaFai Lau 	if (copy_to_sockptr(optlen, &len, sizeof(int)))
14731da177e4SLinus Torvalds 		return -EFAULT;
14746dadbe4bSMartin KaFai Lau 	if (copy_to_sockptr(optval, &val, len))
14751da177e4SLinus Torvalds 		return -EFAULT;
14761da177e4SLinus Torvalds 	return 0;
14771da177e4SLinus Torvalds }
14781da177e4SLinus Torvalds 
ipv6_getsockopt(struct sock * sk,int level,int optname,char __user * optval,int __user * optlen)14793fdadf7dSDmitry Mishin int ipv6_getsockopt(struct sock *sk, int level, int optname,
14803fdadf7dSDmitry Mishin 		    char __user *optval, int __user *optlen)
14813fdadf7dSDmitry Mishin {
14823fdadf7dSDmitry Mishin 	int err;
14833fdadf7dSDmitry Mishin 
14843fdadf7dSDmitry Mishin 	if (level == SOL_IP && sk->sk_type != SOCK_RAW)
14853fdadf7dSDmitry Mishin 		return udp_prot.getsockopt(sk, level, optname, optval, optlen);
14863fdadf7dSDmitry Mishin 
14873fdadf7dSDmitry Mishin 	if (level != SOL_IPV6)
14883fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
14893fdadf7dSDmitry Mishin 
14906dadbe4bSMartin KaFai Lau 	err = do_ipv6_getsockopt(sk, level, optname,
14916dadbe4bSMartin KaFai Lau 				 USER_SOCKPTR(optval), USER_SOCKPTR(optlen));
14923fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
1493cf6fc4a9SWei Yongjun 	/* we need to exclude all possible ENOPROTOOPTs except default case */
1494cf6fc4a9SWei Yongjun 	if (err == -ENOPROTOOPT && optname != IPV6_2292PKTOPTIONS) {
14953fdadf7dSDmitry Mishin 		int len;
14963fdadf7dSDmitry Mishin 
14973fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
14983fdadf7dSDmitry Mishin 			return -EFAULT;
14993fdadf7dSDmitry Mishin 
150001ea306fSPaolo Abeni 		err = nf_getsockopt(sk, PF_INET6, optname, optval, &len);
15013fdadf7dSDmitry Mishin 		if (err >= 0)
15023fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
15033fdadf7dSDmitry Mishin 	}
15043fdadf7dSDmitry Mishin #endif
15053fdadf7dSDmitry Mishin 	return err;
15063fdadf7dSDmitry Mishin }
15077159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ipv6_getsockopt);
1508