xref: /openbmc/linux/net/ipv6/ipv6_sockglue.c (revision b45a337f)
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 
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 
102e7712f1aSYOSHIFUJI Hideaki struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
103e7712f1aSYOSHIFUJI Hideaki 					   struct ipv6_txoptions *opt)
104e7712f1aSYOSHIFUJI Hideaki {
105e7712f1aSYOSHIFUJI Hideaki 	if (inet_sk(sk)->is_icsk) {
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
467e6848976SArnaldo Carvalho de Melo 			/*
468e6848976SArnaldo Carvalho de Melo 			 * Sock is moving from IPv6 to IPv4 (sk_prot), so
469e6848976SArnaldo Carvalho de Melo 			 * remove it from the refcnt debug socks count in the
470e6848976SArnaldo Carvalho de Melo 			 * original family...
471e6848976SArnaldo Carvalho de Melo 			 */
472e6848976SArnaldo Carvalho de Melo 			sk_refcnt_debug_dec(sk);
473e6848976SArnaldo Carvalho de Melo 
4741da177e4SLinus Torvalds 			if (sk->sk_protocol == IPPROTO_TCP) {
475d83d8461SArnaldo Carvalho de Melo 				struct inet_connection_sock *icsk = inet_csk(sk);
476b3cb764aSEric Dumazet 
477c29a0bc4SPavel Emelyanov 				sock_prot_inuse_add(net, sk->sk_prot, -1);
478c29a0bc4SPavel Emelyanov 				sock_prot_inuse_add(net, &tcp_prot, 1);
479b3cb764aSEric Dumazet 
480364f997bSKuniyuki Iwashima 				/* Paired with READ_ONCE(sk->sk_prot) in inet6_stream_ops */
481086d4905SEric Dumazet 				WRITE_ONCE(sk->sk_prot, &tcp_prot);
482f49cd2f4SKuniyuki Iwashima 				/* Paired with READ_ONCE() in tcp_(get|set)sockopt() */
483f49cd2f4SKuniyuki Iwashima 				WRITE_ONCE(icsk->icsk_af_ops, &ipv4_specific);
4841da177e4SLinus Torvalds 				sk->sk_socket->ops = &inet_stream_ops;
4851da177e4SLinus Torvalds 				sk->sk_family = PF_INET;
486d83d8461SArnaldo Carvalho de Melo 				tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
4871da177e4SLinus Torvalds 			} else {
488ba4e58ecSGerrit Renker 				struct proto *prot = &udp_prot;
489ba4e58ecSGerrit Renker 
490db8dac20SDavid S. Miller 				if (sk->sk_protocol == IPPROTO_UDPLITE)
491ba4e58ecSGerrit Renker 					prot = &udplite_prot;
492b3cb764aSEric Dumazet 
493c29a0bc4SPavel Emelyanov 				sock_prot_inuse_add(net, sk->sk_prot, -1);
494c29a0bc4SPavel Emelyanov 				sock_prot_inuse_add(net, prot, 1);
495b3cb764aSEric Dumazet 
496364f997bSKuniyuki Iwashima 				/* Paired with READ_ONCE(sk->sk_prot) in inet6_dgram_ops */
497086d4905SEric Dumazet 				WRITE_ONCE(sk->sk_prot, prot);
4981da177e4SLinus Torvalds 				sk->sk_socket->ops = &inet_dgram_ops;
4991da177e4SLinus Torvalds 				sk->sk_family = PF_INET;
5001da177e4SLinus Torvalds 			}
50121985f43SKuniyuki Iwashima 
50221985f43SKuniyuki Iwashima 			/* Disable all options not to allocate memory anymore,
50321985f43SKuniyuki Iwashima 			 * but there is still a race.  See the lockless path
50421985f43SKuniyuki Iwashima 			 * in udpv6_sendmsg() and ipv6_local_rxpmtu().
50521985f43SKuniyuki Iwashima 			 */
50621985f43SKuniyuki Iwashima 			np->rxopt.all = 0;
50721985f43SKuniyuki Iwashima 
50821985f43SKuniyuki Iwashima 			inet6_cleanup_sock(sk);
5091da177e4SLinus Torvalds 
510e6848976SArnaldo Carvalho de Melo 			/*
511e6848976SArnaldo Carvalho de Melo 			 * ... and add it to the refcnt debug socks count
512e6848976SArnaldo Carvalho de Melo 			 * in the new family. -acme
513e6848976SArnaldo Carvalho de Melo 			 */
514e6848976SArnaldo Carvalho de Melo 			sk_refcnt_debug_inc(sk);
5151da177e4SLinus Torvalds 			module_put(THIS_MODULE);
5161da177e4SLinus Torvalds 			retv = 0;
5171da177e4SLinus Torvalds 			break;
5181da177e4SLinus Torvalds 		}
5191da177e4SLinus Torvalds 		goto e_inval;
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds 	case IPV6_V6ONLY:
522b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int) ||
523c720c7e8SEric Dumazet 		    inet_sk(sk)->inet_num)
5241da177e4SLinus Torvalds 			goto e_inval;
5259fe516baSEric Dumazet 		sk->sk_ipv6only = valbool;
5261da177e4SLinus Torvalds 		retv = 0;
5271da177e4SLinus Torvalds 		break;
5281da177e4SLinus Torvalds 
529333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVPKTINFO:
530b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
531b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5321da177e4SLinus Torvalds 		np->rxopt.bits.rxinfo = valbool;
5331da177e4SLinus Torvalds 		retv = 0;
5341da177e4SLinus Torvalds 		break;
5351da177e4SLinus Torvalds 
536333fad53SYOSHIFUJI Hideaki 	case IPV6_2292PKTINFO:
537b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
538b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
539333fad53SYOSHIFUJI Hideaki 		np->rxopt.bits.rxoinfo = valbool;
540333fad53SYOSHIFUJI Hideaki 		retv = 0;
541333fad53SYOSHIFUJI Hideaki 		break;
542333fad53SYOSHIFUJI Hideaki 
543333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVHOPLIMIT:
544b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
545b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5461da177e4SLinus Torvalds 		np->rxopt.bits.rxhlim = valbool;
5471da177e4SLinus Torvalds 		retv = 0;
5481da177e4SLinus Torvalds 		break;
5491da177e4SLinus Torvalds 
550333fad53SYOSHIFUJI Hideaki 	case IPV6_2292HOPLIMIT:
551b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
552b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
553333fad53SYOSHIFUJI Hideaki 		np->rxopt.bits.rxohlim = valbool;
554333fad53SYOSHIFUJI Hideaki 		retv = 0;
555333fad53SYOSHIFUJI Hideaki 		break;
556333fad53SYOSHIFUJI Hideaki 
557333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVRTHDR:
558b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
559b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5604c752098SYOSHIFUJI Hideaki 		np->rxopt.bits.srcrt = valbool;
5611da177e4SLinus Torvalds 		retv = 0;
5621da177e4SLinus Torvalds 		break;
5631da177e4SLinus Torvalds 
564333fad53SYOSHIFUJI Hideaki 	case IPV6_2292RTHDR:
565b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
566b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5674c752098SYOSHIFUJI Hideaki 		np->rxopt.bits.osrcrt = valbool;
568333fad53SYOSHIFUJI Hideaki 		retv = 0;
569333fad53SYOSHIFUJI Hideaki 		break;
570333fad53SYOSHIFUJI Hideaki 
571333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVHOPOPTS:
572b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
573b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5741da177e4SLinus Torvalds 		np->rxopt.bits.hopopts = valbool;
5751da177e4SLinus Torvalds 		retv = 0;
5761da177e4SLinus Torvalds 		break;
5771da177e4SLinus Torvalds 
578333fad53SYOSHIFUJI Hideaki 	case IPV6_2292HOPOPTS:
579b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
580b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
581333fad53SYOSHIFUJI Hideaki 		np->rxopt.bits.ohopopts = valbool;
582333fad53SYOSHIFUJI Hideaki 		retv = 0;
583333fad53SYOSHIFUJI Hideaki 		break;
584333fad53SYOSHIFUJI Hideaki 
585333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVDSTOPTS:
586b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
587b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5881da177e4SLinus Torvalds 		np->rxopt.bits.dstopts = valbool;
5891da177e4SLinus Torvalds 		retv = 0;
5901da177e4SLinus Torvalds 		break;
5911da177e4SLinus Torvalds 
592333fad53SYOSHIFUJI Hideaki 	case IPV6_2292DSTOPTS:
593b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
594b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
595333fad53SYOSHIFUJI Hideaki 		np->rxopt.bits.odstopts = valbool;
596333fad53SYOSHIFUJI Hideaki 		retv = 0;
597333fad53SYOSHIFUJI Hideaki 		break;
598333fad53SYOSHIFUJI Hideaki 
59941a1f8eaSYOSHIFUJI Hideaki 	case IPV6_TCLASS:
600b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
601b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
602d0ee011fSRemi Denis-Courmont 		if (val < -1 || val > 0xff)
60341a1f8eaSYOSHIFUJI Hideaki 			goto e_inval;
60426ced1e4SGerrit Renker 		/* RFC 3542, 6.5: default traffic class of 0x0 */
60526ced1e4SGerrit Renker 		if (val == -1)
60626ced1e4SGerrit Renker 			val = 0;
6079f7b3a69SMaciej Żenczykowski 		if (sk->sk_type == SOCK_STREAM) {
6089f7b3a69SMaciej Żenczykowski 			val &= ~INET_ECN_MASK;
6099f7b3a69SMaciej Żenczykowski 			val |= np->tclass & INET_ECN_MASK;
6109f7b3a69SMaciej Żenczykowski 		}
611305e95bbSMaciej Żenczykowski 		if (np->tclass != val) {
61241a1f8eaSYOSHIFUJI Hideaki 			np->tclass = val;
613305e95bbSMaciej Żenczykowski 			sk_dst_reset(sk);
614305e95bbSMaciej Żenczykowski 		}
61541a1f8eaSYOSHIFUJI Hideaki 		retv = 0;
61641a1f8eaSYOSHIFUJI Hideaki 		break;
61741a1f8eaSYOSHIFUJI Hideaki 
61841a1f8eaSYOSHIFUJI Hideaki 	case IPV6_RECVTCLASS:
619b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
620b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
62141a1f8eaSYOSHIFUJI Hideaki 		np->rxopt.bits.rxtclass = valbool;
62241a1f8eaSYOSHIFUJI Hideaki 		retv = 0;
62341a1f8eaSYOSHIFUJI Hideaki 		break;
62441a1f8eaSYOSHIFUJI Hideaki 
6251da177e4SLinus Torvalds 	case IPV6_FLOWINFO:
626b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
627b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
6281da177e4SLinus Torvalds 		np->rxopt.bits.rxflow = valbool;
6291da177e4SLinus Torvalds 		retv = 0;
6301da177e4SLinus Torvalds 		break;
6311da177e4SLinus Torvalds 
632793b1473SBrian Haley 	case IPV6_RECVPATHMTU:
633793b1473SBrian Haley 		if (optlen < sizeof(int))
634793b1473SBrian Haley 			goto e_inval;
635793b1473SBrian Haley 		np->rxopt.bits.rxpmtu = valbool;
636793b1473SBrian Haley 		retv = 0;
637793b1473SBrian Haley 		break;
638793b1473SBrian Haley 
6396c468622SBalazs Scheidler 	case IPV6_TRANSPARENT:
64040cd308eSMartin KaFai Lau 		if (valbool && !sockopt_ns_capable(net->user_ns, CAP_NET_RAW) &&
64140cd308eSMartin KaFai Lau 		    !sockopt_ns_capable(net->user_ns, CAP_NET_ADMIN)) {
642b889416bSBalazs Scheidler 			retv = -EPERM;
643b889416bSBalazs Scheidler 			break;
644b889416bSBalazs Scheidler 		}
6456c468622SBalazs Scheidler 		if (optlen < sizeof(int))
6466c468622SBalazs Scheidler 			goto e_inval;
6476c468622SBalazs Scheidler 		/* we don't have a separate transparent bit for IPV6 we use the one in the IPv4 socket */
6486c468622SBalazs Scheidler 		inet_sk(sk)->transparent = valbool;
6496c468622SBalazs Scheidler 		retv = 0;
6506c468622SBalazs Scheidler 		break;
6516c468622SBalazs Scheidler 
65284e14fe3SMaciej Żenczykowski 	case IPV6_FREEBIND:
65384e14fe3SMaciej Żenczykowski 		if (optlen < sizeof(int))
65484e14fe3SMaciej Żenczykowski 			goto e_inval;
65584e14fe3SMaciej Żenczykowski 		/* we also don't have a separate freebind bit for IPV6 */
65684e14fe3SMaciej Żenczykowski 		inet_sk(sk)->freebind = valbool;
65784e14fe3SMaciej Żenczykowski 		retv = 0;
65884e14fe3SMaciej Żenczykowski 		break;
65984e14fe3SMaciej Żenczykowski 
6606c468622SBalazs Scheidler 	case IPV6_RECVORIGDSTADDR:
6616c468622SBalazs Scheidler 		if (optlen < sizeof(int))
6626c468622SBalazs Scheidler 			goto e_inval;
6636c468622SBalazs Scheidler 		np->rxopt.bits.rxorigdstaddr = valbool;
6646c468622SBalazs Scheidler 		retv = 0;
6656c468622SBalazs Scheidler 		break;
6666c468622SBalazs Scheidler 
667333fad53SYOSHIFUJI Hideaki 	case IPV6_HOPOPTS:
668333fad53SYOSHIFUJI Hideaki 	case IPV6_RTHDRDSTOPTS:
669333fad53SYOSHIFUJI Hideaki 	case IPV6_RTHDR:
670333fad53SYOSHIFUJI Hideaki 	case IPV6_DSTOPTS:
671b84d2b73SChristoph Hellwig 		retv = ipv6_set_opt_hdr(sk, optname, optval, optlen);
672a9ba23d4SPaul Moore 		break;
673aea7427fSShan Wei 
674b24a2516SYang Hongyang 	case IPV6_PKTINFO:
675b24a2516SYang Hongyang 	{
676b24a2516SYang Hongyang 		struct in6_pktinfo pkt;
677b24a2516SYang Hongyang 
678b24a2516SYang Hongyang 		if (optlen == 0)
679b24a2516SYang Hongyang 			goto e_inval;
680894cfbc0SChristoph Hellwig 		else if (optlen < sizeof(struct in6_pktinfo) ||
681894cfbc0SChristoph Hellwig 			 sockptr_is_null(optval))
682b24a2516SYang Hongyang 			goto e_inval;
683b24a2516SYang Hongyang 
684894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(&pkt, optval, sizeof(pkt))) {
685b24a2516SYang Hongyang 			retv = -EFAULT;
686b24a2516SYang Hongyang 			break;
687b24a2516SYang Hongyang 		}
688d839a0ebSMike Manning 		if (!sk_dev_equal_l3scope(sk, pkt.ipi6_ifindex))
689b24a2516SYang Hongyang 			goto e_inval;
690b24a2516SYang Hongyang 
691b24a2516SYang Hongyang 		np->sticky_pktinfo.ipi6_ifindex = pkt.ipi6_ifindex;
6924e3fd7a0SAlexey Dobriyan 		np->sticky_pktinfo.ipi6_addr = pkt.ipi6_addr;
693b24a2516SYang Hongyang 		retv = 0;
694b24a2516SYang Hongyang 		break;
695b24a2516SYang Hongyang 	}
696b24a2516SYang Hongyang 
697333fad53SYOSHIFUJI Hideaki 	case IPV6_2292PKTOPTIONS:
6981da177e4SLinus Torvalds 	{
6991da177e4SLinus Torvalds 		struct ipv6_txoptions *opt = NULL;
7001da177e4SLinus Torvalds 		struct msghdr msg;
7014c9483b2SDavid S. Miller 		struct flowi6 fl6;
70226879da5SWei Wang 		struct ipcm6_cookie ipc6;
7031da177e4SLinus Torvalds 
7044c9483b2SDavid S. Miller 		memset(&fl6, 0, sizeof(fl6));
7054c9483b2SDavid S. Miller 		fl6.flowi6_oif = sk->sk_bound_dev_if;
7064c9483b2SDavid S. Miller 		fl6.flowi6_mark = sk->sk_mark;
7071da177e4SLinus Torvalds 
7081da177e4SLinus Torvalds 		if (optlen == 0)
7091da177e4SLinus Torvalds 			goto update;
7101da177e4SLinus Torvalds 
7111da177e4SLinus Torvalds 		/* 1K is probably excessive
7121da177e4SLinus Torvalds 		 * 1K is surely not enough, 2K per standard header is 16K.
7131da177e4SLinus Torvalds 		 */
7141da177e4SLinus Torvalds 		retv = -EINVAL;
7151da177e4SLinus Torvalds 		if (optlen > 64*1024)
7161da177e4SLinus Torvalds 			break;
7171da177e4SLinus Torvalds 
7181da177e4SLinus Torvalds 		opt = sock_kmalloc(sk, sizeof(*opt) + optlen, GFP_KERNEL);
7191da177e4SLinus Torvalds 		retv = -ENOBUFS;
72063159f29SIan Morris 		if (!opt)
7211da177e4SLinus Torvalds 			break;
7221da177e4SLinus Torvalds 
7231da177e4SLinus Torvalds 		memset(opt, 0, sizeof(*opt));
7240aeea21aSReshetova, Elena 		refcount_set(&opt->refcnt, 1);
7251da177e4SLinus Torvalds 		opt->tot_len = sizeof(*opt) + optlen;
7261da177e4SLinus Torvalds 		retv = -EFAULT;
727894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(opt + 1, optval, optlen))
7281da177e4SLinus Torvalds 			goto done;
7291da177e4SLinus Torvalds 
7301da177e4SLinus Torvalds 		msg.msg_controllen = optlen;
7311da177e4SLinus Torvalds 		msg.msg_control = (void *)(opt+1);
73226879da5SWei Wang 		ipc6.opt = opt;
7331da177e4SLinus Torvalds 
7345fdaa88dSWillem de Bruijn 		retv = ip6_datagram_send_ctl(net, sk, &msg, &fl6, &ipc6);
7351da177e4SLinus Torvalds 		if (retv)
7361da177e4SLinus Torvalds 			goto done;
7371da177e4SLinus Torvalds update:
7381da177e4SLinus Torvalds 		retv = 0;
739e7712f1aSYOSHIFUJI Hideaki 		opt = ipv6_update_options(sk, opt);
7401da177e4SLinus Torvalds done:
74145f6fad8SEric Dumazet 		if (opt) {
74245f6fad8SEric Dumazet 			atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
74345f6fad8SEric Dumazet 			txopt_put(opt);
74445f6fad8SEric Dumazet 		}
7451da177e4SLinus Torvalds 		break;
7461da177e4SLinus Torvalds 	}
7471da177e4SLinus Torvalds 	case IPV6_UNICAST_HOPS:
748b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
749b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
7501da177e4SLinus Torvalds 		if (val > 255 || val < -1)
7511da177e4SLinus Torvalds 			goto e_inval;
7521da177e4SLinus Torvalds 		np->hop_limit = val;
7531da177e4SLinus Torvalds 		retv = 0;
7541da177e4SLinus Torvalds 		break;
7551da177e4SLinus Torvalds 
7561da177e4SLinus Torvalds 	case IPV6_MULTICAST_HOPS:
7571da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
7581717699cSYOSHIFUJI Hideaki 			break;
759b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
760b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
7611da177e4SLinus Torvalds 		if (val > 255 || val < -1)
7621da177e4SLinus Torvalds 			goto e_inval;
7632a38e6d5SLi Wei 		np->mcast_hops = (val == -1 ? IPV6_DEFAULT_MCASTHOPS : val);
7641da177e4SLinus Torvalds 		retv = 0;
7651da177e4SLinus Torvalds 		break;
7661da177e4SLinus Torvalds 
7671da177e4SLinus Torvalds 	case IPV6_MULTICAST_LOOP:
768b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
769b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
77028d44882SYOSHIFUJI Hideaki 		if (val != valbool)
77128d44882SYOSHIFUJI Hideaki 			goto e_inval;
7721da177e4SLinus Torvalds 		np->mc_loop = valbool;
7731da177e4SLinus Torvalds 		retv = 0;
7741da177e4SLinus Torvalds 		break;
7751da177e4SLinus Torvalds 
776c4062dfcSErich E. Hoover 	case IPV6_UNICAST_IF:
777c4062dfcSErich E. Hoover 	{
778c4062dfcSErich E. Hoover 		struct net_device *dev = NULL;
779c4062dfcSErich E. Hoover 		int ifindex;
780c4062dfcSErich E. Hoover 
781c4062dfcSErich E. Hoover 		if (optlen != sizeof(int))
782c4062dfcSErich E. Hoover 			goto e_inval;
783c4062dfcSErich E. Hoover 
784c4062dfcSErich E. Hoover 		ifindex = (__force int)ntohl((__force __be32)val);
785c4062dfcSErich E. Hoover 		if (ifindex == 0) {
786c4062dfcSErich E. Hoover 			np->ucast_oif = 0;
787c4062dfcSErich E. Hoover 			retv = 0;
788c4062dfcSErich E. Hoover 			break;
789c4062dfcSErich E. Hoover 		}
790c4062dfcSErich E. Hoover 
791c4062dfcSErich E. Hoover 		dev = dev_get_by_index(net, ifindex);
792c4062dfcSErich E. Hoover 		retv = -EADDRNOTAVAIL;
793c4062dfcSErich E. Hoover 		if (!dev)
794c4062dfcSErich E. Hoover 			break;
795c4062dfcSErich E. Hoover 		dev_put(dev);
796c4062dfcSErich E. Hoover 
797c4062dfcSErich E. Hoover 		retv = -EINVAL;
798c4062dfcSErich E. Hoover 		if (sk->sk_bound_dev_if)
799c4062dfcSErich E. Hoover 			break;
800c4062dfcSErich E. Hoover 
801c4062dfcSErich E. Hoover 		np->ucast_oif = ifindex;
802c4062dfcSErich E. Hoover 		retv = 0;
803c4062dfcSErich E. Hoover 		break;
804c4062dfcSErich E. Hoover 	}
805c4062dfcSErich E. Hoover 
8061da177e4SLinus Torvalds 	case IPV6_MULTICAST_IF:
8071da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
8081717699cSYOSHIFUJI Hideaki 			break;
809b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
810b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
8114953f0fcSBrian Haley 
8124953f0fcSBrian Haley 		if (val) {
81355b80503SEric Dumazet 			struct net_device *dev;
8147bb387c5SDavid Ahern 			int midx;
81555b80503SEric Dumazet 
8167bb387c5SDavid Ahern 			rcu_read_lock();
8171da177e4SLinus Torvalds 
8187bb387c5SDavid Ahern 			dev = dev_get_by_index_rcu(net, val);
81955b80503SEric Dumazet 			if (!dev) {
8207bb387c5SDavid Ahern 				rcu_read_unlock();
8211da177e4SLinus Torvalds 				retv = -ENODEV;
8221da177e4SLinus Torvalds 				break;
8231da177e4SLinus Torvalds 			}
8247bb387c5SDavid Ahern 			midx = l3mdev_master_ifindex_rcu(dev);
8257bb387c5SDavid Ahern 
8267bb387c5SDavid Ahern 			rcu_read_unlock();
8277bb387c5SDavid Ahern 
8287bb387c5SDavid Ahern 			if (sk->sk_bound_dev_if &&
8297bb387c5SDavid Ahern 			    sk->sk_bound_dev_if != val &&
8307bb387c5SDavid Ahern 			    (!midx || midx != sk->sk_bound_dev_if))
8317bb387c5SDavid Ahern 				goto e_inval;
8324953f0fcSBrian Haley 		}
8331da177e4SLinus Torvalds 		np->mcast_oif = val;
8341da177e4SLinus Torvalds 		retv = 0;
8351da177e4SLinus Torvalds 		break;
8361da177e4SLinus Torvalds 	case IPV6_ADD_MEMBERSHIP:
8371da177e4SLinus Torvalds 	case IPV6_DROP_MEMBERSHIP:
8381da177e4SLinus Torvalds 	{
8391da177e4SLinus Torvalds 		struct ipv6_mreq mreq;
8401da177e4SLinus Torvalds 
841a28398baSWang Chen 		if (optlen < sizeof(struct ipv6_mreq))
842a28398baSWang Chen 			goto e_inval;
843a28398baSWang Chen 
844a96fb49bSFlavio Leitner 		retv = -EPROTO;
845a96fb49bSFlavio Leitner 		if (inet_sk(sk)->is_icsk)
846a96fb49bSFlavio Leitner 			break;
847a96fb49bSFlavio Leitner 
8481da177e4SLinus Torvalds 		retv = -EFAULT;
849894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(&mreq, optval, sizeof(struct ipv6_mreq)))
8501da177e4SLinus Torvalds 			break;
8511da177e4SLinus Torvalds 
8521da177e4SLinus Torvalds 		if (optname == IPV6_ADD_MEMBERSHIP)
85354ff9ef3SMarcelo Ricardo Leitner 			retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
8541da177e4SLinus Torvalds 		else
85554ff9ef3SMarcelo Ricardo Leitner 			retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
8561da177e4SLinus Torvalds 		break;
8571da177e4SLinus Torvalds 	}
8581da177e4SLinus Torvalds 	case IPV6_JOIN_ANYCAST:
8591da177e4SLinus Torvalds 	case IPV6_LEAVE_ANYCAST:
8601da177e4SLinus Torvalds 	{
8611da177e4SLinus Torvalds 		struct ipv6_mreq mreq;
8621da177e4SLinus Torvalds 
863a28398baSWang Chen 		if (optlen < sizeof(struct ipv6_mreq))
8641da177e4SLinus Torvalds 			goto e_inval;
8651da177e4SLinus Torvalds 
8661da177e4SLinus Torvalds 		retv = -EFAULT;
867894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(&mreq, optval, sizeof(struct ipv6_mreq)))
8681da177e4SLinus Torvalds 			break;
8691da177e4SLinus Torvalds 
8701da177e4SLinus Torvalds 		if (optname == IPV6_JOIN_ANYCAST)
8711da177e4SLinus Torvalds 			retv = ipv6_sock_ac_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
8721da177e4SLinus Torvalds 		else
8731da177e4SLinus Torvalds 			retv = ipv6_sock_ac_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
8741da177e4SLinus Torvalds 		break;
8751da177e4SLinus Torvalds 	}
87615033f04SAndre Naujoks 	case IPV6_MULTICAST_ALL:
87715033f04SAndre Naujoks 		if (optlen < sizeof(int))
87815033f04SAndre Naujoks 			goto e_inval;
87915033f04SAndre Naujoks 		np->mc_all = valbool;
88015033f04SAndre Naujoks 		retv = 0;
88115033f04SAndre Naujoks 		break;
88215033f04SAndre Naujoks 
8831da177e4SLinus Torvalds 	case MCAST_JOIN_GROUP:
8841da177e4SLinus Torvalds 	case MCAST_LEAVE_GROUP:
8853021ad52SChristoph Hellwig 		if (in_compat_syscall())
8863021ad52SChristoph Hellwig 			retv = compat_ipv6_mcast_join_leave(sk, optname, optval,
8873021ad52SChristoph Hellwig 							    optlen);
8883021ad52SChristoph Hellwig 		else
8893021ad52SChristoph Hellwig 			retv = ipv6_mcast_join_leave(sk, optname, optval,
8903021ad52SChristoph Hellwig 						     optlen);
8911da177e4SLinus Torvalds 		break;
8921da177e4SLinus Torvalds 	case MCAST_JOIN_SOURCE_GROUP:
8931da177e4SLinus Torvalds 	case MCAST_LEAVE_SOURCE_GROUP:
8941da177e4SLinus Torvalds 	case MCAST_BLOCK_SOURCE:
8951da177e4SLinus Torvalds 	case MCAST_UNBLOCK_SOURCE:
8963021ad52SChristoph Hellwig 		retv = do_ipv6_mcast_group_source(sk, optname, optval, optlen);
8971da177e4SLinus Torvalds 		break;
8981da177e4SLinus Torvalds 	case MCAST_MSFILTER:
8993021ad52SChristoph Hellwig 		if (in_compat_syscall())
9003021ad52SChristoph Hellwig 			retv = compat_ipv6_set_mcast_msfilter(sk, optval,
9013021ad52SChristoph Hellwig 							      optlen);
9023021ad52SChristoph Hellwig 		else
903ca0e65ebSChristoph Hellwig 			retv = ipv6_set_mcast_msfilter(sk, optval, optlen);
9041da177e4SLinus Torvalds 		break;
9051da177e4SLinus Torvalds 	case IPV6_ROUTER_ALERT:
906b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
907b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
908725a8ff0SDenis V. Lunev 		retv = ip6_ra_control(sk, val);
9091da177e4SLinus Torvalds 		break;
9109036b2feSFrancesco Ruggeri 	case IPV6_ROUTER_ALERT_ISOLATE:
9119036b2feSFrancesco Ruggeri 		if (optlen < sizeof(int))
9129036b2feSFrancesco Ruggeri 			goto e_inval;
9139036b2feSFrancesco Ruggeri 		np->rtalert_isolate = valbool;
9149036b2feSFrancesco Ruggeri 		retv = 0;
9159036b2feSFrancesco Ruggeri 		break;
9161da177e4SLinus Torvalds 	case IPV6_MTU_DISCOVER:
917b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
918b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
9190b95227aSHannes Frederic Sowa 		if (val < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_OMIT)
9201da177e4SLinus Torvalds 			goto e_inval;
9211da177e4SLinus Torvalds 		np->pmtudisc = val;
9221da177e4SLinus Torvalds 		retv = 0;
9231da177e4SLinus Torvalds 		break;
9241da177e4SLinus Torvalds 	case IPV6_MTU:
925b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
926b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
9271da177e4SLinus Torvalds 		if (val && val < IPV6_MIN_MTU)
9281da177e4SLinus Torvalds 			goto e_inval;
9291da177e4SLinus Torvalds 		np->frag_size = val;
9301da177e4SLinus Torvalds 		retv = 0;
9311da177e4SLinus Torvalds 		break;
9321da177e4SLinus Torvalds 	case IPV6_RECVERR:
933b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
934b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
9351da177e4SLinus Torvalds 		np->recverr = valbool;
9361da177e4SLinus Torvalds 		if (!val)
9371da177e4SLinus Torvalds 			skb_queue_purge(&sk->sk_error_queue);
9381da177e4SLinus Torvalds 		retv = 0;
9391da177e4SLinus Torvalds 		break;
9401da177e4SLinus Torvalds 	case IPV6_FLOWINFO_SEND:
941b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
942b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
9431da177e4SLinus Torvalds 		np->sndflow = valbool;
9441da177e4SLinus Torvalds 		retv = 0;
9451da177e4SLinus Torvalds 		break;
9461da177e4SLinus Torvalds 	case IPV6_FLOWLABEL_MGR:
947894cfbc0SChristoph Hellwig 		retv = ipv6_flowlabel_opt(sk, optval, optlen);
9481da177e4SLinus Torvalds 		break;
9491da177e4SLinus Torvalds 	case IPV6_IPSEC_POLICY:
9501da177e4SLinus Torvalds 	case IPV6_XFRM_POLICY:
9516fc0b4a7SHerbert Xu 		retv = -EPERM;
95240cd308eSMartin KaFai Lau 		if (!sockopt_ns_capable(net->user_ns, CAP_NET_ADMIN))
9536fc0b4a7SHerbert Xu 			break;
954894cfbc0SChristoph Hellwig 		retv = xfrm_user_policy(sk, optname, optval, optlen);
9551da177e4SLinus Torvalds 		break;
9561da177e4SLinus Torvalds 
9577cbca67cSYOSHIFUJI Hideaki 	case IPV6_ADDR_PREFERENCES:
958b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
959b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
96018d5ad62SChristoph Hellwig 		retv = __ip6_sock_set_addr_preferences(sk, val);
9617cbca67cSYOSHIFUJI Hideaki 		break;
962e802af9cSStephen Hemminger 	case IPV6_MINHOPCOUNT:
963e802af9cSStephen Hemminger 		if (optlen < sizeof(int))
964e802af9cSStephen Hemminger 			goto e_inval;
965e802af9cSStephen Hemminger 		if (val < 0 || val > 255)
966e802af9cSStephen Hemminger 			goto e_inval;
967790eb673SEric Dumazet 
968790eb673SEric Dumazet 		if (val)
969790eb673SEric Dumazet 			static_branch_enable(&ip6_min_hopcount);
970790eb673SEric Dumazet 
971cc17c3c8SEric Dumazet 		/* tcp_v6_err() and tcp_v6_rcv() might read min_hopcount
972cc17c3c8SEric Dumazet 		 * while we are changing it.
973cc17c3c8SEric Dumazet 		 */
974cc17c3c8SEric Dumazet 		WRITE_ONCE(np->min_hopcount, val);
975d4596badSHannes Frederic Sowa 		retv = 0;
976793b1473SBrian Haley 		break;
977793b1473SBrian Haley 	case IPV6_DONTFRAG:
978793b1473SBrian Haley 		np->dontfrag = valbool;
979e802af9cSStephen Hemminger 		retv = 0;
980e802af9cSStephen Hemminger 		break;
981cb1ce2efSTom Herbert 	case IPV6_AUTOFLOWLABEL:
982cb1ce2efSTom Herbert 		np->autoflowlabel = valbool;
983513674b5SShaohua Li 		np->autoflowlabel_set = 1;
984cb1ce2efSTom Herbert 		retv = 0;
985cb1ce2efSTom Herbert 		break;
9860cc0aa61SWillem de Bruijn 	case IPV6_RECVFRAGSIZE:
9870cc0aa61SWillem de Bruijn 		np->rxopt.bits.recvfragsize = valbool;
9880cc0aa61SWillem de Bruijn 		retv = 0;
9890cc0aa61SWillem de Bruijn 		break;
99001370434SWillem de Bruijn 	case IPV6_RECVERR_RFC4884:
99101370434SWillem de Bruijn 		if (optlen < sizeof(int))
99201370434SWillem de Bruijn 			goto e_inval;
99301370434SWillem de Bruijn 		if (val < 0 || val > 1)
99401370434SWillem de Bruijn 			goto e_inval;
99501370434SWillem de Bruijn 		np->recverr_rfc4884 = valbool;
99601370434SWillem de Bruijn 		retv = 0;
99701370434SWillem de Bruijn 		break;
9987cbca67cSYOSHIFUJI Hideaki 	}
9997cbca67cSYOSHIFUJI Hideaki 
10003c52c6bbSKuniyuki Iwashima unlock:
100140cd308eSMartin KaFai Lau 	sockopt_release_sock(sk);
1002baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1003baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
10041da177e4SLinus Torvalds 
10051da177e4SLinus Torvalds 	return retv;
10061da177e4SLinus Torvalds 
10071da177e4SLinus Torvalds e_inval:
1008*b45a337fSKuniyuki Iwashima 	retv = -EINVAL;
1009*b45a337fSKuniyuki Iwashima 	goto unlock;
10101da177e4SLinus Torvalds }
10111da177e4SLinus Torvalds 
1012a7b75c5aSChristoph Hellwig int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
1013a7b75c5aSChristoph Hellwig 		    unsigned int optlen)
10143fdadf7dSDmitry Mishin {
10153fdadf7dSDmitry Mishin 	int err;
10163fdadf7dSDmitry Mishin 
10173fdadf7dSDmitry Mishin 	if (level == SOL_IP && sk->sk_type != SOCK_RAW)
10183fdadf7dSDmitry Mishin 		return udp_prot.setsockopt(sk, level, optname, optval, optlen);
10193fdadf7dSDmitry Mishin 
10203fdadf7dSDmitry Mishin 	if (level != SOL_IPV6)
10213fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
10223fdadf7dSDmitry Mishin 
1023a7b75c5aSChristoph Hellwig 	err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
10243fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
10253fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
10263fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IPV6_IPSEC_POLICY &&
10273f34cfaeSPaolo Abeni 			optname != IPV6_XFRM_POLICY)
1028a7b75c5aSChristoph Hellwig 		err = nf_setsockopt(sk, PF_INET6, optname, optval, optlen);
10293fdadf7dSDmitry Mishin #endif
10303fdadf7dSDmitry Mishin 	return err;
10313fdadf7dSDmitry Mishin }
10327159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ipv6_setsockopt);
10333fdadf7dSDmitry Mishin 
103428693079SDavid S. Miller static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
10356dadbe4bSMartin KaFai Lau 				  int optname, sockptr_t optval, int len)
1036333fad53SYOSHIFUJI Hideaki {
103728693079SDavid S. Miller 	struct ipv6_opt_hdr *hdr;
103828693079SDavid S. Miller 
10394c6510a7SYOSHIFUJI Hideaki 	if (!opt)
1040333fad53SYOSHIFUJI Hideaki 		return 0;
10414c6510a7SYOSHIFUJI Hideaki 
10424c6510a7SYOSHIFUJI Hideaki 	switch (optname) {
10434c6510a7SYOSHIFUJI Hideaki 	case IPV6_HOPOPTS:
104428693079SDavid S. Miller 		hdr = opt->hopopt;
10454c6510a7SYOSHIFUJI Hideaki 		break;
10464c6510a7SYOSHIFUJI Hideaki 	case IPV6_RTHDRDSTOPTS:
10474c6510a7SYOSHIFUJI Hideaki 		hdr = opt->dst0opt;
10484c6510a7SYOSHIFUJI Hideaki 		break;
10494c6510a7SYOSHIFUJI Hideaki 	case IPV6_RTHDR:
10504c6510a7SYOSHIFUJI Hideaki 		hdr = (struct ipv6_opt_hdr *)opt->srcrt;
10514c6510a7SYOSHIFUJI Hideaki 		break;
10524c6510a7SYOSHIFUJI Hideaki 	case IPV6_DSTOPTS:
10534c6510a7SYOSHIFUJI Hideaki 		hdr = opt->dst1opt;
10544c6510a7SYOSHIFUJI Hideaki 		break;
10554c6510a7SYOSHIFUJI Hideaki 	default:
10564c6510a7SYOSHIFUJI Hideaki 		return -EINVAL;	/* should not happen */
10574c6510a7SYOSHIFUJI Hideaki 	}
10584c6510a7SYOSHIFUJI Hideaki 
10594c6510a7SYOSHIFUJI Hideaki 	if (!hdr)
10604c6510a7SYOSHIFUJI Hideaki 		return 0;
106128693079SDavid S. Miller 
1062d2b02ed9SChris Wright 	len = min_t(unsigned int, len, ipv6_optlen(hdr));
10636dadbe4bSMartin KaFai Lau 	if (copy_to_sockptr(optval, hdr, len))
1064333fad53SYOSHIFUJI Hideaki 		return -EFAULT;
106595b496b6SYang Hongyang 	return len;
1066333fad53SYOSHIFUJI Hideaki }
1067333fad53SYOSHIFUJI Hideaki 
10686dadbe4bSMartin KaFai Lau static int ipv6_get_msfilter(struct sock *sk, sockptr_t optval,
10696dadbe4bSMartin KaFai Lau 			     sockptr_t optlen, int len)
10701da177e4SLinus Torvalds {
1071db243b79SGustavo A. R. Silva 	const int size0 = offsetof(struct group_filter, gf_slist_flex);
1072d5541e85SChristoph Hellwig 	struct group_filter gsf;
1073931ca7abSAl Viro 	int num;
10741da177e4SLinus Torvalds 	int err;
10751da177e4SLinus Torvalds 
1076931ca7abSAl Viro 	if (len < size0)
10771da177e4SLinus Torvalds 		return -EINVAL;
10786dadbe4bSMartin KaFai Lau 	if (copy_from_sockptr(&gsf, optval, size0))
10791da177e4SLinus Torvalds 		return -EFAULT;
108020c61fbdSYOSHIFUJI Hideaki 	if (gsf.gf_group.ss_family != AF_INET6)
108120c61fbdSYOSHIFUJI Hideaki 		return -EADDRNOTAVAIL;
1082931ca7abSAl Viro 	num = gsf.gf_numsrc;
10830f95f7d4SMartin KaFai Lau 	sockopt_lock_sock(sk);
10846dadbe4bSMartin KaFai Lau 	err = ip6_mc_msfget(sk, &gsf, optval, size0);
1085931ca7abSAl Viro 	if (!err) {
1086931ca7abSAl Viro 		if (num > gsf.gf_numsrc)
1087931ca7abSAl Viro 			num = gsf.gf_numsrc;
10886dadbe4bSMartin KaFai Lau 		len = GROUP_FILTER_SIZE(num);
10896dadbe4bSMartin KaFai Lau 		if (copy_to_sockptr(optlen, &len, sizeof(int)) ||
10906dadbe4bSMartin KaFai Lau 		    copy_to_sockptr(optval, &gsf, size0))
1091931ca7abSAl Viro 			err = -EFAULT;
1092931ca7abSAl Viro 	}
10930f95f7d4SMartin KaFai Lau 	sockopt_release_sock(sk);
10941da177e4SLinus Torvalds 	return err;
10951da177e4SLinus Torvalds }
10961da177e4SLinus Torvalds 
10976dadbe4bSMartin KaFai Lau static int compat_ipv6_get_msfilter(struct sock *sk, sockptr_t optval,
10986dadbe4bSMartin KaFai Lau 				    sockptr_t optlen, int len)
1099d5541e85SChristoph Hellwig {
1100db243b79SGustavo A. R. Silva 	const int size0 = offsetof(struct compat_group_filter, gf_slist_flex);
1101d5541e85SChristoph Hellwig 	struct compat_group_filter gf32;
1102d5541e85SChristoph Hellwig 	struct group_filter gf;
11039c3f9707SMartin KaFai Lau 	int err;
1104d5541e85SChristoph Hellwig 	int num;
1105d5541e85SChristoph Hellwig 
1106d5541e85SChristoph Hellwig 	if (len < size0)
1107d5541e85SChristoph Hellwig 		return -EINVAL;
1108d5541e85SChristoph Hellwig 
11096dadbe4bSMartin KaFai Lau 	if (copy_from_sockptr(&gf32, optval, size0))
1110d5541e85SChristoph Hellwig 		return -EFAULT;
1111d5541e85SChristoph Hellwig 	gf.gf_interface = gf32.gf_interface;
1112d5541e85SChristoph Hellwig 	gf.gf_fmode = gf32.gf_fmode;
1113d5541e85SChristoph Hellwig 	num = gf.gf_numsrc = gf32.gf_numsrc;
1114d5541e85SChristoph Hellwig 	gf.gf_group = gf32.gf_group;
1115d5541e85SChristoph Hellwig 
1116d5541e85SChristoph Hellwig 	if (gf.gf_group.ss_family != AF_INET6)
1117d5541e85SChristoph Hellwig 		return -EADDRNOTAVAIL;
1118d5541e85SChristoph Hellwig 
11190f95f7d4SMartin KaFai Lau 	sockopt_lock_sock(sk);
11206dadbe4bSMartin KaFai Lau 	err = ip6_mc_msfget(sk, &gf, optval, size0);
11210f95f7d4SMartin KaFai Lau 	sockopt_release_sock(sk);
1122d5541e85SChristoph Hellwig 	if (err)
1123d5541e85SChristoph Hellwig 		return err;
1124d5541e85SChristoph Hellwig 	if (num > gf.gf_numsrc)
1125d5541e85SChristoph Hellwig 		num = gf.gf_numsrc;
1126d5541e85SChristoph Hellwig 	len = GROUP_FILTER_SIZE(num) - (sizeof(gf)-sizeof(gf32));
11276dadbe4bSMartin KaFai Lau 	if (copy_to_sockptr(optlen, &len, sizeof(int)) ||
11286dadbe4bSMartin KaFai Lau 	    copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_fmode),
11296dadbe4bSMartin KaFai Lau 				   &gf.gf_fmode, sizeof(gf32.gf_fmode)) ||
11306dadbe4bSMartin KaFai Lau 	    copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_numsrc),
11316dadbe4bSMartin KaFai Lau 				   &gf.gf_numsrc, sizeof(gf32.gf_numsrc)))
1132d5541e85SChristoph Hellwig 		return -EFAULT;
1133d5541e85SChristoph Hellwig 	return 0;
1134d5541e85SChristoph Hellwig }
1135d5541e85SChristoph Hellwig 
113638566ec0SMartin KaFai Lau int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
11376dadbe4bSMartin KaFai Lau 		       sockptr_t optval, sockptr_t optlen)
1138d5541e85SChristoph Hellwig {
1139d5541e85SChristoph Hellwig 	struct ipv6_pinfo *np = inet6_sk(sk);
1140d5541e85SChristoph Hellwig 	int len;
1141d5541e85SChristoph Hellwig 	int val;
1142d5541e85SChristoph Hellwig 
1143d5541e85SChristoph Hellwig 	if (ip6_mroute_opt(optname))
1144d5541e85SChristoph Hellwig 		return ip6_mroute_getsockopt(sk, optname, optval, optlen);
1145d5541e85SChristoph Hellwig 
11466dadbe4bSMartin KaFai Lau 	if (copy_from_sockptr(&len, optlen, sizeof(int)))
1147d5541e85SChristoph Hellwig 		return -EFAULT;
1148d5541e85SChristoph Hellwig 	switch (optname) {
1149d5541e85SChristoph Hellwig 	case IPV6_ADDRFORM:
1150d5541e85SChristoph Hellwig 		if (sk->sk_protocol != IPPROTO_UDP &&
1151d5541e85SChristoph Hellwig 		    sk->sk_protocol != IPPROTO_UDPLITE &&
1152d5541e85SChristoph Hellwig 		    sk->sk_protocol != IPPROTO_TCP)
1153d5541e85SChristoph Hellwig 			return -ENOPROTOOPT;
1154d5541e85SChristoph Hellwig 		if (sk->sk_state != TCP_ESTABLISHED)
1155d5541e85SChristoph Hellwig 			return -ENOTCONN;
1156d5541e85SChristoph Hellwig 		val = sk->sk_family;
1157d5541e85SChristoph Hellwig 		break;
1158d5541e85SChristoph Hellwig 	case MCAST_MSFILTER:
11593021ad52SChristoph Hellwig 		if (in_compat_syscall())
11609c3f9707SMartin KaFai Lau 			return compat_ipv6_get_msfilter(sk, optval, optlen, len);
1161d5541e85SChristoph Hellwig 		return ipv6_get_msfilter(sk, optval, optlen, len);
1162333fad53SYOSHIFUJI Hideaki 	case IPV6_2292PKTOPTIONS:
11631da177e4SLinus Torvalds 	{
11641da177e4SLinus Torvalds 		struct msghdr msg;
11651da177e4SLinus Torvalds 		struct sk_buff *skb;
11661da177e4SLinus Torvalds 
11671da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_STREAM)
11681da177e4SLinus Torvalds 			return -ENOPROTOOPT;
11691da177e4SLinus Torvalds 
11706dadbe4bSMartin KaFai Lau 		if (optval.is_kernel) {
11716dadbe4bSMartin KaFai Lau 			msg.msg_control_is_user = false;
11726dadbe4bSMartin KaFai Lau 			msg.msg_control = optval.kernel;
11736dadbe4bSMartin KaFai Lau 		} else {
11741b2f08dfSChristoph Hellwig 			msg.msg_control_is_user = true;
11756dadbe4bSMartin KaFai Lau 			msg.msg_control_user = optval.user;
11766dadbe4bSMartin KaFai Lau 		}
11771da177e4SLinus Torvalds 		msg.msg_controllen = len;
117875f23979SMartin KaFai Lau 		msg.msg_flags = 0;
11791da177e4SLinus Torvalds 
11800f95f7d4SMartin KaFai Lau 		sockopt_lock_sock(sk);
11811da177e4SLinus Torvalds 		skb = np->pktoptions;
11821da177e4SLinus Torvalds 		if (skb)
11834b261c75SHannes Frederic Sowa 			ip6_datagram_recv_ctl(sk, &msg, skb);
11840f95f7d4SMartin KaFai Lau 		sockopt_release_sock(sk);
11851dc7b90fSEric Dumazet 		if (!skb) {
11861da177e4SLinus Torvalds 			if (np->rxopt.bits.rxinfo) {
11871da177e4SLinus Torvalds 				struct in6_pktinfo src_info;
1188f250dcdaSYang Hongyang 				src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
1189f250dcdaSYang Hongyang 					np->sticky_pktinfo.ipi6_ifindex;
1190efe4208fSEric Dumazet 				src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr : np->sticky_pktinfo.ipi6_addr;
11911da177e4SLinus Torvalds 				put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
11921da177e4SLinus Torvalds 			}
11931da177e4SLinus Torvalds 			if (np->rxopt.bits.rxhlim) {
11941da177e4SLinus Torvalds 				int hlim = np->mcast_hops;
11951da177e4SLinus Torvalds 				put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
11961da177e4SLinus Torvalds 			}
11974c507d28SJiri Benc 			if (np->rxopt.bits.rxtclass) {
1198d76ed22bSLi RongQing 				int tclass = (int)ip6_tclass(np->rcv_flowinfo);
1199d76ed22bSLi RongQing 
12004c507d28SJiri Benc 				put_cmsg(&msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
12014c507d28SJiri Benc 			}
1202333fad53SYOSHIFUJI Hideaki 			if (np->rxopt.bits.rxoinfo) {
1203333fad53SYOSHIFUJI Hideaki 				struct in6_pktinfo src_info;
1204f250dcdaSYang Hongyang 				src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
1205f250dcdaSYang Hongyang 					np->sticky_pktinfo.ipi6_ifindex;
1206efe4208fSEric Dumazet 				src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr :
1207efe4208fSEric Dumazet 								     np->sticky_pktinfo.ipi6_addr;
1208333fad53SYOSHIFUJI Hideaki 				put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
1209333fad53SYOSHIFUJI Hideaki 			}
1210333fad53SYOSHIFUJI Hideaki 			if (np->rxopt.bits.rxohlim) {
1211333fad53SYOSHIFUJI Hideaki 				int hlim = np->mcast_hops;
1212333fad53SYOSHIFUJI Hideaki 				put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
1213333fad53SYOSHIFUJI Hideaki 			}
12141397ed35SFlorent Fourcot 			if (np->rxopt.bits.rxflow) {
121568536053SFlorent Fourcot 				__be32 flowinfo = np->rcv_flowinfo;
121668536053SFlorent Fourcot 
12171397ed35SFlorent Fourcot 				put_cmsg(&msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
12181397ed35SFlorent Fourcot 			}
12191da177e4SLinus Torvalds 		}
12201da177e4SLinus Torvalds 		len -= msg.msg_controllen;
12216dadbe4bSMartin KaFai Lau 		return copy_to_sockptr(optlen, &len, sizeof(int));
12221da177e4SLinus Torvalds 	}
12231da177e4SLinus Torvalds 	case IPV6_MTU:
12241da177e4SLinus Torvalds 	{
12251da177e4SLinus Torvalds 		struct dst_entry *dst;
1226b6c6712aSEric Dumazet 
12271da177e4SLinus Torvalds 		val = 0;
1228b6c6712aSEric Dumazet 		rcu_read_lock();
1229b6c6712aSEric Dumazet 		dst = __sk_dst_get(sk);
1230b6c6712aSEric Dumazet 		if (dst)
12311da177e4SLinus Torvalds 			val = dst_mtu(dst);
1232b6c6712aSEric Dumazet 		rcu_read_unlock();
12331da177e4SLinus Torvalds 		if (!val)
12341da177e4SLinus Torvalds 			return -ENOTCONN;
12351da177e4SLinus Torvalds 		break;
12361da177e4SLinus Torvalds 	}
12371da177e4SLinus Torvalds 
12381da177e4SLinus Torvalds 	case IPV6_V6ONLY:
12399fe516baSEric Dumazet 		val = sk->sk_ipv6only;
12401da177e4SLinus Torvalds 		break;
12411da177e4SLinus Torvalds 
1242333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVPKTINFO:
12431da177e4SLinus Torvalds 		val = np->rxopt.bits.rxinfo;
12441da177e4SLinus Torvalds 		break;
12451da177e4SLinus Torvalds 
1246333fad53SYOSHIFUJI Hideaki 	case IPV6_2292PKTINFO:
1247333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.rxoinfo;
1248333fad53SYOSHIFUJI Hideaki 		break;
1249333fad53SYOSHIFUJI Hideaki 
1250333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVHOPLIMIT:
12511da177e4SLinus Torvalds 		val = np->rxopt.bits.rxhlim;
12521da177e4SLinus Torvalds 		break;
12531da177e4SLinus Torvalds 
1254333fad53SYOSHIFUJI Hideaki 	case IPV6_2292HOPLIMIT:
1255333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.rxohlim;
1256333fad53SYOSHIFUJI Hideaki 		break;
1257333fad53SYOSHIFUJI Hideaki 
1258333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVRTHDR:
12591da177e4SLinus Torvalds 		val = np->rxopt.bits.srcrt;
12601da177e4SLinus Torvalds 		break;
12611da177e4SLinus Torvalds 
1262333fad53SYOSHIFUJI Hideaki 	case IPV6_2292RTHDR:
1263333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.osrcrt;
1264333fad53SYOSHIFUJI Hideaki 		break;
1265333fad53SYOSHIFUJI Hideaki 
12661da177e4SLinus Torvalds 	case IPV6_HOPOPTS:
1267333fad53SYOSHIFUJI Hideaki 	case IPV6_RTHDRDSTOPTS:
1268333fad53SYOSHIFUJI Hideaki 	case IPV6_RTHDR:
1269333fad53SYOSHIFUJI Hideaki 	case IPV6_DSTOPTS:
1270333fad53SYOSHIFUJI Hideaki 	{
127145f6fad8SEric Dumazet 		struct ipv6_txoptions *opt;
1272333fad53SYOSHIFUJI Hideaki 
12730f95f7d4SMartin KaFai Lau 		sockopt_lock_sock(sk);
12741e1d04e6SHannes Frederic Sowa 		opt = rcu_dereference_protected(np->opt,
12751e1d04e6SHannes Frederic Sowa 						lockdep_sock_is_held(sk));
127645f6fad8SEric Dumazet 		len = ipv6_getsockopt_sticky(sk, opt, optname, optval, len);
12770f95f7d4SMartin KaFai Lau 		sockopt_release_sock(sk);
127805335c22SYang Hongyang 		/* check if ipv6_getsockopt_sticky() returns err code */
127905335c22SYang Hongyang 		if (len < 0)
128005335c22SYang Hongyang 			return len;
12816dadbe4bSMartin KaFai Lau 		return copy_to_sockptr(optlen, &len, sizeof(int));
1282333fad53SYOSHIFUJI Hideaki 	}
1283333fad53SYOSHIFUJI Hideaki 
1284333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVHOPOPTS:
12851da177e4SLinus Torvalds 		val = np->rxopt.bits.hopopts;
12861da177e4SLinus Torvalds 		break;
12871da177e4SLinus Torvalds 
1288333fad53SYOSHIFUJI Hideaki 	case IPV6_2292HOPOPTS:
1289333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.ohopopts;
1290333fad53SYOSHIFUJI Hideaki 		break;
1291333fad53SYOSHIFUJI Hideaki 
1292333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVDSTOPTS:
12931da177e4SLinus Torvalds 		val = np->rxopt.bits.dstopts;
12941da177e4SLinus Torvalds 		break;
12951da177e4SLinus Torvalds 
1296333fad53SYOSHIFUJI Hideaki 	case IPV6_2292DSTOPTS:
1297333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.odstopts;
1298333fad53SYOSHIFUJI Hideaki 		break;
1299333fad53SYOSHIFUJI Hideaki 
130041a1f8eaSYOSHIFUJI Hideaki 	case IPV6_TCLASS:
130141a1f8eaSYOSHIFUJI Hideaki 		val = np->tclass;
130241a1f8eaSYOSHIFUJI Hideaki 		break;
130341a1f8eaSYOSHIFUJI Hideaki 
130441a1f8eaSYOSHIFUJI Hideaki 	case IPV6_RECVTCLASS:
130541a1f8eaSYOSHIFUJI Hideaki 		val = np->rxopt.bits.rxtclass;
130641a1f8eaSYOSHIFUJI Hideaki 		break;
130741a1f8eaSYOSHIFUJI Hideaki 
13081da177e4SLinus Torvalds 	case IPV6_FLOWINFO:
13091da177e4SLinus Torvalds 		val = np->rxopt.bits.rxflow;
13101da177e4SLinus Torvalds 		break;
13111da177e4SLinus Torvalds 
1312793b1473SBrian Haley 	case IPV6_RECVPATHMTU:
1313793b1473SBrian Haley 		val = np->rxopt.bits.rxpmtu;
1314793b1473SBrian Haley 		break;
1315793b1473SBrian Haley 
1316793b1473SBrian Haley 	case IPV6_PATHMTU:
1317793b1473SBrian Haley 	{
1318793b1473SBrian Haley 		struct dst_entry *dst;
1319793b1473SBrian Haley 		struct ip6_mtuinfo mtuinfo;
1320793b1473SBrian Haley 
1321793b1473SBrian Haley 		if (len < sizeof(mtuinfo))
1322793b1473SBrian Haley 			return -EINVAL;
1323793b1473SBrian Haley 
1324793b1473SBrian Haley 		len = sizeof(mtuinfo);
1325793b1473SBrian Haley 		memset(&mtuinfo, 0, sizeof(mtuinfo));
1326793b1473SBrian Haley 
1327793b1473SBrian Haley 		rcu_read_lock();
1328793b1473SBrian Haley 		dst = __sk_dst_get(sk);
1329793b1473SBrian Haley 		if (dst)
1330793b1473SBrian Haley 			mtuinfo.ip6m_mtu = dst_mtu(dst);
1331793b1473SBrian Haley 		rcu_read_unlock();
1332793b1473SBrian Haley 		if (!mtuinfo.ip6m_mtu)
1333793b1473SBrian Haley 			return -ENOTCONN;
1334793b1473SBrian Haley 
13356dadbe4bSMartin KaFai Lau 		if (copy_to_sockptr(optlen, &len, sizeof(int)))
1336793b1473SBrian Haley 			return -EFAULT;
13376dadbe4bSMartin KaFai Lau 		if (copy_to_sockptr(optval, &mtuinfo, len))
1338793b1473SBrian Haley 			return -EFAULT;
1339793b1473SBrian Haley 
1340793b1473SBrian Haley 		return 0;
1341793b1473SBrian Haley 	}
1342793b1473SBrian Haley 
13436c468622SBalazs Scheidler 	case IPV6_TRANSPARENT:
13446c468622SBalazs Scheidler 		val = inet_sk(sk)->transparent;
13456c468622SBalazs Scheidler 		break;
13466c468622SBalazs Scheidler 
134784e14fe3SMaciej Żenczykowski 	case IPV6_FREEBIND:
134884e14fe3SMaciej Żenczykowski 		val = inet_sk(sk)->freebind;
134984e14fe3SMaciej Żenczykowski 		break;
135084e14fe3SMaciej Żenczykowski 
13516c468622SBalazs Scheidler 	case IPV6_RECVORIGDSTADDR:
13526c468622SBalazs Scheidler 		val = np->rxopt.bits.rxorigdstaddr;
13536c468622SBalazs Scheidler 		break;
13546c468622SBalazs Scheidler 
13551da177e4SLinus Torvalds 	case IPV6_UNICAST_HOPS:
13561da177e4SLinus Torvalds 	case IPV6_MULTICAST_HOPS:
1357befffe90SBrian Haley 	{
1358befffe90SBrian Haley 		struct dst_entry *dst;
1359befffe90SBrian Haley 
1360befffe90SBrian Haley 		if (optname == IPV6_UNICAST_HOPS)
1361befffe90SBrian Haley 			val = np->hop_limit;
1362befffe90SBrian Haley 		else
13631da177e4SLinus Torvalds 			val = np->mcast_hops;
1364befffe90SBrian Haley 
1365b6c6712aSEric Dumazet 		if (val < 0) {
1366b6c6712aSEric Dumazet 			rcu_read_lock();
1367b6c6712aSEric Dumazet 			dst = __sk_dst_get(sk);
1368b6c6712aSEric Dumazet 			if (dst)
13696b75d090SYOSHIFUJI Hideaki 				val = ip6_dst_hoplimit(dst);
1370b6c6712aSEric Dumazet 			rcu_read_unlock();
1371befffe90SBrian Haley 		}
1372b6c6712aSEric Dumazet 
1373befffe90SBrian Haley 		if (val < 0)
137453b7997fSYOSHIFUJI Hideaki 			val = sock_net(sk)->ipv6.devconf_all->hop_limit;
13751da177e4SLinus Torvalds 		break;
1376befffe90SBrian Haley 	}
13771da177e4SLinus Torvalds 
13781da177e4SLinus Torvalds 	case IPV6_MULTICAST_LOOP:
13791da177e4SLinus Torvalds 		val = np->mc_loop;
13801da177e4SLinus Torvalds 		break;
13811da177e4SLinus Torvalds 
13821da177e4SLinus Torvalds 	case IPV6_MULTICAST_IF:
13831da177e4SLinus Torvalds 		val = np->mcast_oif;
13841da177e4SLinus Torvalds 		break;
13851da177e4SLinus Torvalds 
138615033f04SAndre Naujoks 	case IPV6_MULTICAST_ALL:
138715033f04SAndre Naujoks 		val = np->mc_all;
138815033f04SAndre Naujoks 		break;
138915033f04SAndre Naujoks 
1390c4062dfcSErich E. Hoover 	case IPV6_UNICAST_IF:
1391c4062dfcSErich E. Hoover 		val = (__force int)htonl((__u32) np->ucast_oif);
1392c4062dfcSErich E. Hoover 		break;
1393c4062dfcSErich E. Hoover 
13941da177e4SLinus Torvalds 	case IPV6_MTU_DISCOVER:
13951da177e4SLinus Torvalds 		val = np->pmtudisc;
13961da177e4SLinus Torvalds 		break;
13971da177e4SLinus Torvalds 
13981da177e4SLinus Torvalds 	case IPV6_RECVERR:
13991da177e4SLinus Torvalds 		val = np->recverr;
14001da177e4SLinus Torvalds 		break;
14011da177e4SLinus Torvalds 
14021da177e4SLinus Torvalds 	case IPV6_FLOWINFO_SEND:
14031da177e4SLinus Torvalds 		val = np->sndflow;
14041da177e4SLinus Torvalds 		break;
14051da177e4SLinus Torvalds 
14063fdfa5ffSFlorent Fourcot 	case IPV6_FLOWLABEL_MGR:
14073fdfa5ffSFlorent Fourcot 	{
14083fdfa5ffSFlorent Fourcot 		struct in6_flowlabel_req freq;
140946e5f401SFlorent Fourcot 		int flags;
14103fdfa5ffSFlorent Fourcot 
14113fdfa5ffSFlorent Fourcot 		if (len < sizeof(freq))
14123fdfa5ffSFlorent Fourcot 			return -EINVAL;
14133fdfa5ffSFlorent Fourcot 
14146dadbe4bSMartin KaFai Lau 		if (copy_from_sockptr(&freq, optval, sizeof(freq)))
14153fdfa5ffSFlorent Fourcot 			return -EFAULT;
14163fdfa5ffSFlorent Fourcot 
14173fdfa5ffSFlorent Fourcot 		if (freq.flr_action != IPV6_FL_A_GET)
14183fdfa5ffSFlorent Fourcot 			return -EINVAL;
14193fdfa5ffSFlorent Fourcot 
14203fdfa5ffSFlorent Fourcot 		len = sizeof(freq);
142146e5f401SFlorent Fourcot 		flags = freq.flr_flags;
142246e5f401SFlorent Fourcot 
14233fdfa5ffSFlorent Fourcot 		memset(&freq, 0, sizeof(freq));
14243fdfa5ffSFlorent Fourcot 
142546e5f401SFlorent Fourcot 		val = ipv6_flowlabel_opt_get(sk, &freq, flags);
14263fdfa5ffSFlorent Fourcot 		if (val < 0)
14273fdfa5ffSFlorent Fourcot 			return val;
14283fdfa5ffSFlorent Fourcot 
14296dadbe4bSMartin KaFai Lau 		if (copy_to_sockptr(optlen, &len, sizeof(int)))
14303fdfa5ffSFlorent Fourcot 			return -EFAULT;
14316dadbe4bSMartin KaFai Lau 		if (copy_to_sockptr(optval, &freq, len))
14323fdfa5ffSFlorent Fourcot 			return -EFAULT;
14333fdfa5ffSFlorent Fourcot 
14343fdfa5ffSFlorent Fourcot 		return 0;
14353fdfa5ffSFlorent Fourcot 	}
14363fdfa5ffSFlorent Fourcot 
14377cbca67cSYOSHIFUJI Hideaki 	case IPV6_ADDR_PREFERENCES:
14387cbca67cSYOSHIFUJI Hideaki 		val = 0;
14397cbca67cSYOSHIFUJI Hideaki 
14407cbca67cSYOSHIFUJI Hideaki 		if (np->srcprefs & IPV6_PREFER_SRC_TMP)
14417cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_TMP;
14427cbca67cSYOSHIFUJI Hideaki 		else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC)
14437cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_PUBLIC;
14447cbca67cSYOSHIFUJI Hideaki 		else {
14457cbca67cSYOSHIFUJI Hideaki 			/* XXX: should we return system default? */
14467cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT;
14477cbca67cSYOSHIFUJI Hideaki 		}
14487cbca67cSYOSHIFUJI Hideaki 
14497cbca67cSYOSHIFUJI Hideaki 		if (np->srcprefs & IPV6_PREFER_SRC_COA)
14507cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_COA;
14517cbca67cSYOSHIFUJI Hideaki 		else
14527cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_HOME;
14537cbca67cSYOSHIFUJI Hideaki 		break;
14547cbca67cSYOSHIFUJI Hideaki 
1455e802af9cSStephen Hemminger 	case IPV6_MINHOPCOUNT:
1456e802af9cSStephen Hemminger 		val = np->min_hopcount;
1457e802af9cSStephen Hemminger 		break;
1458e802af9cSStephen Hemminger 
1459793b1473SBrian Haley 	case IPV6_DONTFRAG:
1460793b1473SBrian Haley 		val = np->dontfrag;
1461793b1473SBrian Haley 		break;
1462793b1473SBrian Haley 
1463cb1ce2efSTom Herbert 	case IPV6_AUTOFLOWLABEL:
1464e9191ffbSBen Hutchings 		val = ip6_autoflowlabel(sock_net(sk), np);
1465cb1ce2efSTom Herbert 		break;
1466cb1ce2efSTom Herbert 
14670cc0aa61SWillem de Bruijn 	case IPV6_RECVFRAGSIZE:
14680cc0aa61SWillem de Bruijn 		val = np->rxopt.bits.recvfragsize;
14690cc0aa61SWillem de Bruijn 		break;
14700cc0aa61SWillem de Bruijn 
14719036b2feSFrancesco Ruggeri 	case IPV6_ROUTER_ALERT_ISOLATE:
14729036b2feSFrancesco Ruggeri 		val = np->rtalert_isolate;
14739036b2feSFrancesco Ruggeri 		break;
14749036b2feSFrancesco Ruggeri 
147501370434SWillem de Bruijn 	case IPV6_RECVERR_RFC4884:
147601370434SWillem de Bruijn 		val = np->recverr_rfc4884;
147701370434SWillem de Bruijn 		break;
147801370434SWillem de Bruijn 
14791da177e4SLinus Torvalds 	default:
1480cf6fc4a9SWei Yongjun 		return -ENOPROTOOPT;
14811da177e4SLinus Torvalds 	}
14821da177e4SLinus Torvalds 	len = min_t(unsigned int, sizeof(int), len);
14836dadbe4bSMartin KaFai Lau 	if (copy_to_sockptr(optlen, &len, sizeof(int)))
14841da177e4SLinus Torvalds 		return -EFAULT;
14856dadbe4bSMartin KaFai Lau 	if (copy_to_sockptr(optval, &val, len))
14861da177e4SLinus Torvalds 		return -EFAULT;
14871da177e4SLinus Torvalds 	return 0;
14881da177e4SLinus Torvalds }
14891da177e4SLinus Torvalds 
14903fdadf7dSDmitry Mishin int ipv6_getsockopt(struct sock *sk, int level, int optname,
14913fdadf7dSDmitry Mishin 		    char __user *optval, int __user *optlen)
14923fdadf7dSDmitry Mishin {
14933fdadf7dSDmitry Mishin 	int err;
14943fdadf7dSDmitry Mishin 
14953fdadf7dSDmitry Mishin 	if (level == SOL_IP && sk->sk_type != SOCK_RAW)
14963fdadf7dSDmitry Mishin 		return udp_prot.getsockopt(sk, level, optname, optval, optlen);
14973fdadf7dSDmitry Mishin 
14983fdadf7dSDmitry Mishin 	if (level != SOL_IPV6)
14993fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
15003fdadf7dSDmitry Mishin 
15016dadbe4bSMartin KaFai Lau 	err = do_ipv6_getsockopt(sk, level, optname,
15026dadbe4bSMartin KaFai Lau 				 USER_SOCKPTR(optval), USER_SOCKPTR(optlen));
15033fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
1504cf6fc4a9SWei Yongjun 	/* we need to exclude all possible ENOPROTOOPTs except default case */
1505cf6fc4a9SWei Yongjun 	if (err == -ENOPROTOOPT && optname != IPV6_2292PKTOPTIONS) {
15063fdadf7dSDmitry Mishin 		int len;
15073fdadf7dSDmitry Mishin 
15083fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
15093fdadf7dSDmitry Mishin 			return -EFAULT;
15103fdadf7dSDmitry Mishin 
151101ea306fSPaolo Abeni 		err = nf_getsockopt(sk, PF_INET6, optname, optval, &len);
15123fdadf7dSDmitry Mishin 		if (err >= 0)
15133fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
15143fdadf7dSDmitry Mishin 	}
15153fdadf7dSDmitry Mishin #endif
15163fdadf7dSDmitry Mishin 	return err;
15173fdadf7dSDmitry Mishin }
15187159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ipv6_getsockopt);
1519