xref: /openbmc/linux/net/ipv6/ipv6_sockglue.c (revision 7de6d09f)
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;
213*7de6d09fSKuniyuki 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;
247*7de6d09fSKuniyuki 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 */
330b84d2b73SChristoph Hellwig 	if (optname != IPV6_RTHDR && !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 
3943fdadf7dSDmitry Mishin static 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();
4201da177e4SLinus Torvalds 	lock_sock(sk);
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	switch (optname) {
4231da177e4SLinus Torvalds 
4241da177e4SLinus Torvalds 	case IPV6_ADDRFORM:
425b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
426b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
4271da177e4SLinus Torvalds 		if (val == PF_INET) {
4281da177e4SLinus Torvalds 			struct ipv6_txoptions *opt;
4291da177e4SLinus Torvalds 			struct sk_buff *pktopt;
4301da177e4SLinus Torvalds 
43149d074f4SDenis V. Lunev 			if (sk->sk_type == SOCK_RAW)
43249d074f4SDenis V. Lunev 				break;
43349d074f4SDenis V. Lunev 
4349596cc82SDenis V. Lunev 			if (sk->sk_protocol == IPPROTO_UDP ||
4359596cc82SDenis V. Lunev 			    sk->sk_protocol == IPPROTO_UDPLITE) {
4369596cc82SDenis V. Lunev 				struct udp_sock *up = udp_sk(sk);
4379596cc82SDenis V. Lunev 				if (up->pending == AF_INET6) {
4389596cc82SDenis V. Lunev 					retv = -EBUSY;
4399596cc82SDenis V. Lunev 					break;
4409596cc82SDenis V. Lunev 				}
44179a1f0ccSHangbin Liu 			} else if (sk->sk_protocol == IPPROTO_TCP) {
44279a1f0ccSHangbin Liu 				if (sk->sk_prot != &tcpv6_prot) {
443b6f61189SEric Dumazet 					retv = -EBUSY;
4441da177e4SLinus Torvalds 					break;
445b6f61189SEric Dumazet 				}
44679a1f0ccSHangbin Liu 			} else {
447b6f61189SEric Dumazet 				break;
44879a1f0ccSHangbin Liu 			}
44979a1f0ccSHangbin Liu 
4501da177e4SLinus Torvalds 			if (sk->sk_state != TCP_ESTABLISHED) {
4511da177e4SLinus Torvalds 				retv = -ENOTCONN;
4521da177e4SLinus Torvalds 				break;
4531da177e4SLinus Torvalds 			}
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds 			if (ipv6_only_sock(sk) ||
456efe4208fSEric Dumazet 			    !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) {
4571da177e4SLinus Torvalds 				retv = -EADDRNOTAVAIL;
4581da177e4SLinus Torvalds 				break;
4591da177e4SLinus Torvalds 			}
4601da177e4SLinus Torvalds 
4611da177e4SLinus Torvalds 			fl6_free_socklist(sk);
4628651be8fSWANG Cong 			__ipv6_sock_mc_close(sk);
4638c0de6e9SCong Wang 			__ipv6_sock_ac_close(sk);
4641da177e4SLinus Torvalds 
465e6848976SArnaldo Carvalho de Melo 			/*
466e6848976SArnaldo Carvalho de Melo 			 * Sock is moving from IPv6 to IPv4 (sk_prot), so
467e6848976SArnaldo Carvalho de Melo 			 * remove it from the refcnt debug socks count in the
468e6848976SArnaldo Carvalho de Melo 			 * original family...
469e6848976SArnaldo Carvalho de Melo 			 */
470e6848976SArnaldo Carvalho de Melo 			sk_refcnt_debug_dec(sk);
471e6848976SArnaldo Carvalho de Melo 
4721da177e4SLinus Torvalds 			if (sk->sk_protocol == IPPROTO_TCP) {
473d83d8461SArnaldo Carvalho de Melo 				struct inet_connection_sock *icsk = inet_csk(sk);
474b3cb764aSEric Dumazet 
475c29a0bc4SPavel Emelyanov 				sock_prot_inuse_add(net, sk->sk_prot, -1);
476c29a0bc4SPavel Emelyanov 				sock_prot_inuse_add(net, &tcp_prot, 1);
477b3cb764aSEric Dumazet 
478086d4905SEric Dumazet 				/* Paired with READ_ONCE(sk->sk_prot) in net/ipv6/af_inet6.c */
479086d4905SEric Dumazet 				WRITE_ONCE(sk->sk_prot, &tcp_prot);
480d83d8461SArnaldo Carvalho de Melo 				icsk->icsk_af_ops = &ipv4_specific;
4811da177e4SLinus Torvalds 				sk->sk_socket->ops = &inet_stream_ops;
4821da177e4SLinus Torvalds 				sk->sk_family = PF_INET;
483d83d8461SArnaldo Carvalho de Melo 				tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
4841da177e4SLinus Torvalds 			} else {
485ba4e58ecSGerrit Renker 				struct proto *prot = &udp_prot;
486ba4e58ecSGerrit Renker 
487db8dac20SDavid S. Miller 				if (sk->sk_protocol == IPPROTO_UDPLITE)
488ba4e58ecSGerrit Renker 					prot = &udplite_prot;
489b3cb764aSEric Dumazet 
490c29a0bc4SPavel Emelyanov 				sock_prot_inuse_add(net, sk->sk_prot, -1);
491c29a0bc4SPavel Emelyanov 				sock_prot_inuse_add(net, prot, 1);
492b3cb764aSEric Dumazet 
493086d4905SEric Dumazet 				/* Paired with READ_ONCE(sk->sk_prot) in net/ipv6/af_inet6.c */
494086d4905SEric Dumazet 				WRITE_ONCE(sk->sk_prot, prot);
4951da177e4SLinus Torvalds 				sk->sk_socket->ops = &inet_dgram_ops;
4961da177e4SLinus Torvalds 				sk->sk_family = PF_INET;
4971da177e4SLinus Torvalds 			}
49845f6fad8SEric Dumazet 			opt = xchg((__force struct ipv6_txoptions **)&np->opt,
49945f6fad8SEric Dumazet 				   NULL);
50045f6fad8SEric Dumazet 			if (opt) {
50145f6fad8SEric Dumazet 				atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
50245f6fad8SEric Dumazet 				txopt_put(opt);
50345f6fad8SEric Dumazet 			}
5041da177e4SLinus Torvalds 			pktopt = xchg(&np->pktoptions, NULL);
5051da177e4SLinus Torvalds 			kfree_skb(pktopt);
5061da177e4SLinus Torvalds 
507e6848976SArnaldo Carvalho de Melo 			/*
508e6848976SArnaldo Carvalho de Melo 			 * ... and add it to the refcnt debug socks count
509e6848976SArnaldo Carvalho de Melo 			 * in the new family. -acme
510e6848976SArnaldo Carvalho de Melo 			 */
511e6848976SArnaldo Carvalho de Melo 			sk_refcnt_debug_inc(sk);
5121da177e4SLinus Torvalds 			module_put(THIS_MODULE);
5131da177e4SLinus Torvalds 			retv = 0;
5141da177e4SLinus Torvalds 			break;
5151da177e4SLinus Torvalds 		}
5161da177e4SLinus Torvalds 		goto e_inval;
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds 	case IPV6_V6ONLY:
519b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int) ||
520c720c7e8SEric Dumazet 		    inet_sk(sk)->inet_num)
5211da177e4SLinus Torvalds 			goto e_inval;
5229fe516baSEric Dumazet 		sk->sk_ipv6only = valbool;
5231da177e4SLinus Torvalds 		retv = 0;
5241da177e4SLinus Torvalds 		break;
5251da177e4SLinus Torvalds 
526333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVPKTINFO:
527b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
528b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5291da177e4SLinus Torvalds 		np->rxopt.bits.rxinfo = valbool;
5301da177e4SLinus Torvalds 		retv = 0;
5311da177e4SLinus Torvalds 		break;
5321da177e4SLinus Torvalds 
533333fad53SYOSHIFUJI Hideaki 	case IPV6_2292PKTINFO:
534b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
535b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
536333fad53SYOSHIFUJI Hideaki 		np->rxopt.bits.rxoinfo = valbool;
537333fad53SYOSHIFUJI Hideaki 		retv = 0;
538333fad53SYOSHIFUJI Hideaki 		break;
539333fad53SYOSHIFUJI Hideaki 
540333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVHOPLIMIT:
541b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
542b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5431da177e4SLinus Torvalds 		np->rxopt.bits.rxhlim = valbool;
5441da177e4SLinus Torvalds 		retv = 0;
5451da177e4SLinus Torvalds 		break;
5461da177e4SLinus Torvalds 
547333fad53SYOSHIFUJI Hideaki 	case IPV6_2292HOPLIMIT:
548b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
549b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
550333fad53SYOSHIFUJI Hideaki 		np->rxopt.bits.rxohlim = valbool;
551333fad53SYOSHIFUJI Hideaki 		retv = 0;
552333fad53SYOSHIFUJI Hideaki 		break;
553333fad53SYOSHIFUJI Hideaki 
554333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVRTHDR:
555b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
556b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5574c752098SYOSHIFUJI Hideaki 		np->rxopt.bits.srcrt = valbool;
5581da177e4SLinus Torvalds 		retv = 0;
5591da177e4SLinus Torvalds 		break;
5601da177e4SLinus Torvalds 
561333fad53SYOSHIFUJI Hideaki 	case IPV6_2292RTHDR:
562b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
563b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5644c752098SYOSHIFUJI Hideaki 		np->rxopt.bits.osrcrt = valbool;
565333fad53SYOSHIFUJI Hideaki 		retv = 0;
566333fad53SYOSHIFUJI Hideaki 		break;
567333fad53SYOSHIFUJI Hideaki 
568333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVHOPOPTS:
569b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
570b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5711da177e4SLinus Torvalds 		np->rxopt.bits.hopopts = valbool;
5721da177e4SLinus Torvalds 		retv = 0;
5731da177e4SLinus Torvalds 		break;
5741da177e4SLinus Torvalds 
575333fad53SYOSHIFUJI Hideaki 	case IPV6_2292HOPOPTS:
576b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
577b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
578333fad53SYOSHIFUJI Hideaki 		np->rxopt.bits.ohopopts = valbool;
579333fad53SYOSHIFUJI Hideaki 		retv = 0;
580333fad53SYOSHIFUJI Hideaki 		break;
581333fad53SYOSHIFUJI Hideaki 
582333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVDSTOPTS:
583b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
584b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
5851da177e4SLinus Torvalds 		np->rxopt.bits.dstopts = valbool;
5861da177e4SLinus Torvalds 		retv = 0;
5871da177e4SLinus Torvalds 		break;
5881da177e4SLinus Torvalds 
589333fad53SYOSHIFUJI Hideaki 	case IPV6_2292DSTOPTS:
590b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
591b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
592333fad53SYOSHIFUJI Hideaki 		np->rxopt.bits.odstopts = valbool;
593333fad53SYOSHIFUJI Hideaki 		retv = 0;
594333fad53SYOSHIFUJI Hideaki 		break;
595333fad53SYOSHIFUJI Hideaki 
59641a1f8eaSYOSHIFUJI Hideaki 	case IPV6_TCLASS:
597b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
598b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
599d0ee011fSRemi Denis-Courmont 		if (val < -1 || val > 0xff)
60041a1f8eaSYOSHIFUJI Hideaki 			goto e_inval;
60126ced1e4SGerrit Renker 		/* RFC 3542, 6.5: default traffic class of 0x0 */
60226ced1e4SGerrit Renker 		if (val == -1)
60326ced1e4SGerrit Renker 			val = 0;
6049f7b3a69SMaciej Żenczykowski 		if (sk->sk_type == SOCK_STREAM) {
6059f7b3a69SMaciej Żenczykowski 			val &= ~INET_ECN_MASK;
6069f7b3a69SMaciej Żenczykowski 			val |= np->tclass & INET_ECN_MASK;
6079f7b3a69SMaciej Żenczykowski 		}
608305e95bbSMaciej Żenczykowski 		if (np->tclass != val) {
60941a1f8eaSYOSHIFUJI Hideaki 			np->tclass = val;
610305e95bbSMaciej Żenczykowski 			sk_dst_reset(sk);
611305e95bbSMaciej Żenczykowski 		}
61241a1f8eaSYOSHIFUJI Hideaki 		retv = 0;
61341a1f8eaSYOSHIFUJI Hideaki 		break;
61441a1f8eaSYOSHIFUJI Hideaki 
61541a1f8eaSYOSHIFUJI Hideaki 	case IPV6_RECVTCLASS:
616b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
617b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
61841a1f8eaSYOSHIFUJI Hideaki 		np->rxopt.bits.rxtclass = valbool;
61941a1f8eaSYOSHIFUJI Hideaki 		retv = 0;
62041a1f8eaSYOSHIFUJI Hideaki 		break;
62141a1f8eaSYOSHIFUJI Hideaki 
6221da177e4SLinus Torvalds 	case IPV6_FLOWINFO:
623b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
624b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
6251da177e4SLinus Torvalds 		np->rxopt.bits.rxflow = valbool;
6261da177e4SLinus Torvalds 		retv = 0;
6271da177e4SLinus Torvalds 		break;
6281da177e4SLinus Torvalds 
629793b1473SBrian Haley 	case IPV6_RECVPATHMTU:
630793b1473SBrian Haley 		if (optlen < sizeof(int))
631793b1473SBrian Haley 			goto e_inval;
632793b1473SBrian Haley 		np->rxopt.bits.rxpmtu = valbool;
633793b1473SBrian Haley 		retv = 0;
634793b1473SBrian Haley 		break;
635793b1473SBrian Haley 
6366c468622SBalazs Scheidler 	case IPV6_TRANSPARENT:
63735fc59c9SMaciej Żenczykowski 		if (valbool && !ns_capable(net->user_ns, CAP_NET_RAW) &&
63835fc59c9SMaciej Żenczykowski 		    !ns_capable(net->user_ns, CAP_NET_ADMIN)) {
639b889416bSBalazs Scheidler 			retv = -EPERM;
640b889416bSBalazs Scheidler 			break;
641b889416bSBalazs Scheidler 		}
6426c468622SBalazs Scheidler 		if (optlen < sizeof(int))
6436c468622SBalazs Scheidler 			goto e_inval;
6446c468622SBalazs Scheidler 		/* we don't have a separate transparent bit for IPV6 we use the one in the IPv4 socket */
6456c468622SBalazs Scheidler 		inet_sk(sk)->transparent = valbool;
6466c468622SBalazs Scheidler 		retv = 0;
6476c468622SBalazs Scheidler 		break;
6486c468622SBalazs Scheidler 
64984e14fe3SMaciej Żenczykowski 	case IPV6_FREEBIND:
65084e14fe3SMaciej Żenczykowski 		if (optlen < sizeof(int))
65184e14fe3SMaciej Żenczykowski 			goto e_inval;
65284e14fe3SMaciej Żenczykowski 		/* we also don't have a separate freebind bit for IPV6 */
65384e14fe3SMaciej Żenczykowski 		inet_sk(sk)->freebind = valbool;
65484e14fe3SMaciej Żenczykowski 		retv = 0;
65584e14fe3SMaciej Żenczykowski 		break;
65684e14fe3SMaciej Żenczykowski 
6576c468622SBalazs Scheidler 	case IPV6_RECVORIGDSTADDR:
6586c468622SBalazs Scheidler 		if (optlen < sizeof(int))
6596c468622SBalazs Scheidler 			goto e_inval;
6606c468622SBalazs Scheidler 		np->rxopt.bits.rxorigdstaddr = valbool;
6616c468622SBalazs Scheidler 		retv = 0;
6626c468622SBalazs Scheidler 		break;
6636c468622SBalazs Scheidler 
664333fad53SYOSHIFUJI Hideaki 	case IPV6_HOPOPTS:
665333fad53SYOSHIFUJI Hideaki 	case IPV6_RTHDRDSTOPTS:
666333fad53SYOSHIFUJI Hideaki 	case IPV6_RTHDR:
667333fad53SYOSHIFUJI Hideaki 	case IPV6_DSTOPTS:
668b84d2b73SChristoph Hellwig 		retv = ipv6_set_opt_hdr(sk, optname, optval, optlen);
669a9ba23d4SPaul Moore 		break;
670aea7427fSShan Wei 
671b24a2516SYang Hongyang 	case IPV6_PKTINFO:
672b24a2516SYang Hongyang 	{
673b24a2516SYang Hongyang 		struct in6_pktinfo pkt;
674b24a2516SYang Hongyang 
675b24a2516SYang Hongyang 		if (optlen == 0)
676b24a2516SYang Hongyang 			goto e_inval;
677894cfbc0SChristoph Hellwig 		else if (optlen < sizeof(struct in6_pktinfo) ||
678894cfbc0SChristoph Hellwig 			 sockptr_is_null(optval))
679b24a2516SYang Hongyang 			goto e_inval;
680b24a2516SYang Hongyang 
681894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(&pkt, optval, sizeof(pkt))) {
682b24a2516SYang Hongyang 			retv = -EFAULT;
683b24a2516SYang Hongyang 			break;
684b24a2516SYang Hongyang 		}
685d839a0ebSMike Manning 		if (!sk_dev_equal_l3scope(sk, pkt.ipi6_ifindex))
686b24a2516SYang Hongyang 			goto e_inval;
687b24a2516SYang Hongyang 
688b24a2516SYang Hongyang 		np->sticky_pktinfo.ipi6_ifindex = pkt.ipi6_ifindex;
6894e3fd7a0SAlexey Dobriyan 		np->sticky_pktinfo.ipi6_addr = pkt.ipi6_addr;
690b24a2516SYang Hongyang 		retv = 0;
691b24a2516SYang Hongyang 		break;
692b24a2516SYang Hongyang 	}
693b24a2516SYang Hongyang 
694333fad53SYOSHIFUJI Hideaki 	case IPV6_2292PKTOPTIONS:
6951da177e4SLinus Torvalds 	{
6961da177e4SLinus Torvalds 		struct ipv6_txoptions *opt = NULL;
6971da177e4SLinus Torvalds 		struct msghdr msg;
6984c9483b2SDavid S. Miller 		struct flowi6 fl6;
69926879da5SWei Wang 		struct ipcm6_cookie ipc6;
7001da177e4SLinus Torvalds 
7014c9483b2SDavid S. Miller 		memset(&fl6, 0, sizeof(fl6));
7024c9483b2SDavid S. Miller 		fl6.flowi6_oif = sk->sk_bound_dev_if;
7034c9483b2SDavid S. Miller 		fl6.flowi6_mark = sk->sk_mark;
7041da177e4SLinus Torvalds 
7051da177e4SLinus Torvalds 		if (optlen == 0)
7061da177e4SLinus Torvalds 			goto update;
7071da177e4SLinus Torvalds 
7081da177e4SLinus Torvalds 		/* 1K is probably excessive
7091da177e4SLinus Torvalds 		 * 1K is surely not enough, 2K per standard header is 16K.
7101da177e4SLinus Torvalds 		 */
7111da177e4SLinus Torvalds 		retv = -EINVAL;
7121da177e4SLinus Torvalds 		if (optlen > 64*1024)
7131da177e4SLinus Torvalds 			break;
7141da177e4SLinus Torvalds 
7151da177e4SLinus Torvalds 		opt = sock_kmalloc(sk, sizeof(*opt) + optlen, GFP_KERNEL);
7161da177e4SLinus Torvalds 		retv = -ENOBUFS;
71763159f29SIan Morris 		if (!opt)
7181da177e4SLinus Torvalds 			break;
7191da177e4SLinus Torvalds 
7201da177e4SLinus Torvalds 		memset(opt, 0, sizeof(*opt));
7210aeea21aSReshetova, Elena 		refcount_set(&opt->refcnt, 1);
7221da177e4SLinus Torvalds 		opt->tot_len = sizeof(*opt) + optlen;
7231da177e4SLinus Torvalds 		retv = -EFAULT;
724894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(opt + 1, optval, optlen))
7251da177e4SLinus Torvalds 			goto done;
7261da177e4SLinus Torvalds 
7271da177e4SLinus Torvalds 		msg.msg_controllen = optlen;
7281da177e4SLinus Torvalds 		msg.msg_control = (void *)(opt+1);
72926879da5SWei Wang 		ipc6.opt = opt;
7301da177e4SLinus Torvalds 
7315fdaa88dSWillem de Bruijn 		retv = ip6_datagram_send_ctl(net, sk, &msg, &fl6, &ipc6);
7321da177e4SLinus Torvalds 		if (retv)
7331da177e4SLinus Torvalds 			goto done;
7341da177e4SLinus Torvalds update:
7351da177e4SLinus Torvalds 		retv = 0;
736e7712f1aSYOSHIFUJI Hideaki 		opt = ipv6_update_options(sk, opt);
7371da177e4SLinus Torvalds done:
73845f6fad8SEric Dumazet 		if (opt) {
73945f6fad8SEric Dumazet 			atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
74045f6fad8SEric Dumazet 			txopt_put(opt);
74145f6fad8SEric Dumazet 		}
7421da177e4SLinus Torvalds 		break;
7431da177e4SLinus Torvalds 	}
7441da177e4SLinus Torvalds 	case IPV6_UNICAST_HOPS:
745b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
746b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
7471da177e4SLinus Torvalds 		if (val > 255 || val < -1)
7481da177e4SLinus Torvalds 			goto e_inval;
7491da177e4SLinus Torvalds 		np->hop_limit = val;
7501da177e4SLinus Torvalds 		retv = 0;
7511da177e4SLinus Torvalds 		break;
7521da177e4SLinus Torvalds 
7531da177e4SLinus Torvalds 	case IPV6_MULTICAST_HOPS:
7541da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
7551717699cSYOSHIFUJI Hideaki 			break;
756b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
757b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
7581da177e4SLinus Torvalds 		if (val > 255 || val < -1)
7591da177e4SLinus Torvalds 			goto e_inval;
7602a38e6d5SLi Wei 		np->mcast_hops = (val == -1 ? IPV6_DEFAULT_MCASTHOPS : val);
7611da177e4SLinus Torvalds 		retv = 0;
7621da177e4SLinus Torvalds 		break;
7631da177e4SLinus Torvalds 
7641da177e4SLinus Torvalds 	case IPV6_MULTICAST_LOOP:
765b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
766b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
76728d44882SYOSHIFUJI Hideaki 		if (val != valbool)
76828d44882SYOSHIFUJI Hideaki 			goto e_inval;
7691da177e4SLinus Torvalds 		np->mc_loop = valbool;
7701da177e4SLinus Torvalds 		retv = 0;
7711da177e4SLinus Torvalds 		break;
7721da177e4SLinus Torvalds 
773c4062dfcSErich E. Hoover 	case IPV6_UNICAST_IF:
774c4062dfcSErich E. Hoover 	{
775c4062dfcSErich E. Hoover 		struct net_device *dev = NULL;
776c4062dfcSErich E. Hoover 		int ifindex;
777c4062dfcSErich E. Hoover 
778c4062dfcSErich E. Hoover 		if (optlen != sizeof(int))
779c4062dfcSErich E. Hoover 			goto e_inval;
780c4062dfcSErich E. Hoover 
781c4062dfcSErich E. Hoover 		ifindex = (__force int)ntohl((__force __be32)val);
782c4062dfcSErich E. Hoover 		if (ifindex == 0) {
783c4062dfcSErich E. Hoover 			np->ucast_oif = 0;
784c4062dfcSErich E. Hoover 			retv = 0;
785c4062dfcSErich E. Hoover 			break;
786c4062dfcSErich E. Hoover 		}
787c4062dfcSErich E. Hoover 
788c4062dfcSErich E. Hoover 		dev = dev_get_by_index(net, ifindex);
789c4062dfcSErich E. Hoover 		retv = -EADDRNOTAVAIL;
790c4062dfcSErich E. Hoover 		if (!dev)
791c4062dfcSErich E. Hoover 			break;
792c4062dfcSErich E. Hoover 		dev_put(dev);
793c4062dfcSErich E. Hoover 
794c4062dfcSErich E. Hoover 		retv = -EINVAL;
795c4062dfcSErich E. Hoover 		if (sk->sk_bound_dev_if)
796c4062dfcSErich E. Hoover 			break;
797c4062dfcSErich E. Hoover 
798c4062dfcSErich E. Hoover 		np->ucast_oif = ifindex;
799c4062dfcSErich E. Hoover 		retv = 0;
800c4062dfcSErich E. Hoover 		break;
801c4062dfcSErich E. Hoover 	}
802c4062dfcSErich E. Hoover 
8031da177e4SLinus Torvalds 	case IPV6_MULTICAST_IF:
8041da177e4SLinus Torvalds 		if (sk->sk_type == SOCK_STREAM)
8051717699cSYOSHIFUJI Hideaki 			break;
806b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
807b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
8084953f0fcSBrian Haley 
8094953f0fcSBrian Haley 		if (val) {
81055b80503SEric Dumazet 			struct net_device *dev;
8117bb387c5SDavid Ahern 			int midx;
81255b80503SEric Dumazet 
8137bb387c5SDavid Ahern 			rcu_read_lock();
8141da177e4SLinus Torvalds 
8157bb387c5SDavid Ahern 			dev = dev_get_by_index_rcu(net, val);
81655b80503SEric Dumazet 			if (!dev) {
8177bb387c5SDavid Ahern 				rcu_read_unlock();
8181da177e4SLinus Torvalds 				retv = -ENODEV;
8191da177e4SLinus Torvalds 				break;
8201da177e4SLinus Torvalds 			}
8217bb387c5SDavid Ahern 			midx = l3mdev_master_ifindex_rcu(dev);
8227bb387c5SDavid Ahern 
8237bb387c5SDavid Ahern 			rcu_read_unlock();
8247bb387c5SDavid Ahern 
8257bb387c5SDavid Ahern 			if (sk->sk_bound_dev_if &&
8267bb387c5SDavid Ahern 			    sk->sk_bound_dev_if != val &&
8277bb387c5SDavid Ahern 			    (!midx || midx != sk->sk_bound_dev_if))
8287bb387c5SDavid Ahern 				goto e_inval;
8294953f0fcSBrian Haley 		}
8301da177e4SLinus Torvalds 		np->mcast_oif = val;
8311da177e4SLinus Torvalds 		retv = 0;
8321da177e4SLinus Torvalds 		break;
8331da177e4SLinus Torvalds 	case IPV6_ADD_MEMBERSHIP:
8341da177e4SLinus Torvalds 	case IPV6_DROP_MEMBERSHIP:
8351da177e4SLinus Torvalds 	{
8361da177e4SLinus Torvalds 		struct ipv6_mreq mreq;
8371da177e4SLinus Torvalds 
838a28398baSWang Chen 		if (optlen < sizeof(struct ipv6_mreq))
839a28398baSWang Chen 			goto e_inval;
840a28398baSWang Chen 
841a96fb49bSFlavio Leitner 		retv = -EPROTO;
842a96fb49bSFlavio Leitner 		if (inet_sk(sk)->is_icsk)
843a96fb49bSFlavio Leitner 			break;
844a96fb49bSFlavio Leitner 
8451da177e4SLinus Torvalds 		retv = -EFAULT;
846894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(&mreq, optval, sizeof(struct ipv6_mreq)))
8471da177e4SLinus Torvalds 			break;
8481da177e4SLinus Torvalds 
8491da177e4SLinus Torvalds 		if (optname == IPV6_ADD_MEMBERSHIP)
85054ff9ef3SMarcelo Ricardo Leitner 			retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
8511da177e4SLinus Torvalds 		else
85254ff9ef3SMarcelo Ricardo Leitner 			retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
8531da177e4SLinus Torvalds 		break;
8541da177e4SLinus Torvalds 	}
8551da177e4SLinus Torvalds 	case IPV6_JOIN_ANYCAST:
8561da177e4SLinus Torvalds 	case IPV6_LEAVE_ANYCAST:
8571da177e4SLinus Torvalds 	{
8581da177e4SLinus Torvalds 		struct ipv6_mreq mreq;
8591da177e4SLinus Torvalds 
860a28398baSWang Chen 		if (optlen < sizeof(struct ipv6_mreq))
8611da177e4SLinus Torvalds 			goto e_inval;
8621da177e4SLinus Torvalds 
8631da177e4SLinus Torvalds 		retv = -EFAULT;
864894cfbc0SChristoph Hellwig 		if (copy_from_sockptr(&mreq, optval, sizeof(struct ipv6_mreq)))
8651da177e4SLinus Torvalds 			break;
8661da177e4SLinus Torvalds 
8671da177e4SLinus Torvalds 		if (optname == IPV6_JOIN_ANYCAST)
8681da177e4SLinus Torvalds 			retv = ipv6_sock_ac_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
8691da177e4SLinus Torvalds 		else
8701da177e4SLinus Torvalds 			retv = ipv6_sock_ac_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
8711da177e4SLinus Torvalds 		break;
8721da177e4SLinus Torvalds 	}
87315033f04SAndre Naujoks 	case IPV6_MULTICAST_ALL:
87415033f04SAndre Naujoks 		if (optlen < sizeof(int))
87515033f04SAndre Naujoks 			goto e_inval;
87615033f04SAndre Naujoks 		np->mc_all = valbool;
87715033f04SAndre Naujoks 		retv = 0;
87815033f04SAndre Naujoks 		break;
87915033f04SAndre Naujoks 
8801da177e4SLinus Torvalds 	case MCAST_JOIN_GROUP:
8811da177e4SLinus Torvalds 	case MCAST_LEAVE_GROUP:
8823021ad52SChristoph Hellwig 		if (in_compat_syscall())
8833021ad52SChristoph Hellwig 			retv = compat_ipv6_mcast_join_leave(sk, optname, optval,
8843021ad52SChristoph Hellwig 							    optlen);
8853021ad52SChristoph Hellwig 		else
8863021ad52SChristoph Hellwig 			retv = ipv6_mcast_join_leave(sk, optname, optval,
8873021ad52SChristoph Hellwig 						     optlen);
8881da177e4SLinus Torvalds 		break;
8891da177e4SLinus Torvalds 	case MCAST_JOIN_SOURCE_GROUP:
8901da177e4SLinus Torvalds 	case MCAST_LEAVE_SOURCE_GROUP:
8911da177e4SLinus Torvalds 	case MCAST_BLOCK_SOURCE:
8921da177e4SLinus Torvalds 	case MCAST_UNBLOCK_SOURCE:
8933021ad52SChristoph Hellwig 		retv = do_ipv6_mcast_group_source(sk, optname, optval, optlen);
8941da177e4SLinus Torvalds 		break;
8951da177e4SLinus Torvalds 	case MCAST_MSFILTER:
8963021ad52SChristoph Hellwig 		if (in_compat_syscall())
8973021ad52SChristoph Hellwig 			retv = compat_ipv6_set_mcast_msfilter(sk, optval,
8983021ad52SChristoph Hellwig 							      optlen);
8993021ad52SChristoph Hellwig 		else
900ca0e65ebSChristoph Hellwig 			retv = ipv6_set_mcast_msfilter(sk, optval, optlen);
9011da177e4SLinus Torvalds 		break;
9021da177e4SLinus Torvalds 	case IPV6_ROUTER_ALERT:
903b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
904b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
905725a8ff0SDenis V. Lunev 		retv = ip6_ra_control(sk, val);
9061da177e4SLinus Torvalds 		break;
9079036b2feSFrancesco Ruggeri 	case IPV6_ROUTER_ALERT_ISOLATE:
9089036b2feSFrancesco Ruggeri 		if (optlen < sizeof(int))
9099036b2feSFrancesco Ruggeri 			goto e_inval;
9109036b2feSFrancesco Ruggeri 		np->rtalert_isolate = valbool;
9119036b2feSFrancesco Ruggeri 		retv = 0;
9129036b2feSFrancesco Ruggeri 		break;
9131da177e4SLinus Torvalds 	case IPV6_MTU_DISCOVER:
914b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
915b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
9160b95227aSHannes Frederic Sowa 		if (val < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_OMIT)
9171da177e4SLinus Torvalds 			goto e_inval;
9181da177e4SLinus Torvalds 		np->pmtudisc = val;
9191da177e4SLinus Torvalds 		retv = 0;
9201da177e4SLinus Torvalds 		break;
9211da177e4SLinus Torvalds 	case IPV6_MTU:
922b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
923b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
9241da177e4SLinus Torvalds 		if (val && val < IPV6_MIN_MTU)
9251da177e4SLinus Torvalds 			goto e_inval;
9261da177e4SLinus Torvalds 		np->frag_size = val;
9271da177e4SLinus Torvalds 		retv = 0;
9281da177e4SLinus Torvalds 		break;
9291da177e4SLinus Torvalds 	case IPV6_RECVERR:
930b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
931b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
9321da177e4SLinus Torvalds 		np->recverr = valbool;
9331da177e4SLinus Torvalds 		if (!val)
9341da177e4SLinus Torvalds 			skb_queue_purge(&sk->sk_error_queue);
9351da177e4SLinus Torvalds 		retv = 0;
9361da177e4SLinus Torvalds 		break;
9371da177e4SLinus Torvalds 	case IPV6_FLOWINFO_SEND:
938b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
939b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
9401da177e4SLinus Torvalds 		np->sndflow = valbool;
9411da177e4SLinus Torvalds 		retv = 0;
9421da177e4SLinus Torvalds 		break;
9431da177e4SLinus Torvalds 	case IPV6_FLOWLABEL_MGR:
944894cfbc0SChristoph Hellwig 		retv = ipv6_flowlabel_opt(sk, optval, optlen);
9451da177e4SLinus Torvalds 		break;
9461da177e4SLinus Torvalds 	case IPV6_IPSEC_POLICY:
9471da177e4SLinus Torvalds 	case IPV6_XFRM_POLICY:
9486fc0b4a7SHerbert Xu 		retv = -EPERM;
949af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
9506fc0b4a7SHerbert Xu 			break;
951894cfbc0SChristoph Hellwig 		retv = xfrm_user_policy(sk, optname, optval, optlen);
9521da177e4SLinus Torvalds 		break;
9531da177e4SLinus Torvalds 
9547cbca67cSYOSHIFUJI Hideaki 	case IPV6_ADDR_PREFERENCES:
955b2a9d7c2SYOSHIFUJI Hideaki 		if (optlen < sizeof(int))
956b2a9d7c2SYOSHIFUJI Hideaki 			goto e_inval;
95718d5ad62SChristoph Hellwig 		retv = __ip6_sock_set_addr_preferences(sk, val);
9587cbca67cSYOSHIFUJI Hideaki 		break;
959e802af9cSStephen Hemminger 	case IPV6_MINHOPCOUNT:
960e802af9cSStephen Hemminger 		if (optlen < sizeof(int))
961e802af9cSStephen Hemminger 			goto e_inval;
962e802af9cSStephen Hemminger 		if (val < 0 || val > 255)
963e802af9cSStephen Hemminger 			goto e_inval;
964790eb673SEric Dumazet 
965790eb673SEric Dumazet 		if (val)
966790eb673SEric Dumazet 			static_branch_enable(&ip6_min_hopcount);
967790eb673SEric Dumazet 
968cc17c3c8SEric Dumazet 		/* tcp_v6_err() and tcp_v6_rcv() might read min_hopcount
969cc17c3c8SEric Dumazet 		 * while we are changing it.
970cc17c3c8SEric Dumazet 		 */
971cc17c3c8SEric Dumazet 		WRITE_ONCE(np->min_hopcount, val);
972d4596badSHannes Frederic Sowa 		retv = 0;
973793b1473SBrian Haley 		break;
974793b1473SBrian Haley 	case IPV6_DONTFRAG:
975793b1473SBrian Haley 		np->dontfrag = valbool;
976e802af9cSStephen Hemminger 		retv = 0;
977e802af9cSStephen Hemminger 		break;
978cb1ce2efSTom Herbert 	case IPV6_AUTOFLOWLABEL:
979cb1ce2efSTom Herbert 		np->autoflowlabel = valbool;
980513674b5SShaohua Li 		np->autoflowlabel_set = 1;
981cb1ce2efSTom Herbert 		retv = 0;
982cb1ce2efSTom Herbert 		break;
9830cc0aa61SWillem de Bruijn 	case IPV6_RECVFRAGSIZE:
9840cc0aa61SWillem de Bruijn 		np->rxopt.bits.recvfragsize = valbool;
9850cc0aa61SWillem de Bruijn 		retv = 0;
9860cc0aa61SWillem de Bruijn 		break;
98701370434SWillem de Bruijn 	case IPV6_RECVERR_RFC4884:
98801370434SWillem de Bruijn 		if (optlen < sizeof(int))
98901370434SWillem de Bruijn 			goto e_inval;
99001370434SWillem de Bruijn 		if (val < 0 || val > 1)
99101370434SWillem de Bruijn 			goto e_inval;
99201370434SWillem de Bruijn 		np->recverr_rfc4884 = valbool;
99301370434SWillem de Bruijn 		retv = 0;
99401370434SWillem de Bruijn 		break;
9957cbca67cSYOSHIFUJI Hideaki 	}
9967cbca67cSYOSHIFUJI Hideaki 
9971da177e4SLinus Torvalds 	release_sock(sk);
998baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
999baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
10001da177e4SLinus Torvalds 
10011da177e4SLinus Torvalds 	return retv;
10021da177e4SLinus Torvalds 
10031da177e4SLinus Torvalds e_inval:
10041da177e4SLinus Torvalds 	release_sock(sk);
1005baf606d9SMarcelo Ricardo Leitner 	if (needs_rtnl)
1006baf606d9SMarcelo Ricardo Leitner 		rtnl_unlock();
10071da177e4SLinus Torvalds 	return -EINVAL;
10081da177e4SLinus Torvalds }
10091da177e4SLinus Torvalds 
1010a7b75c5aSChristoph Hellwig int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
1011a7b75c5aSChristoph Hellwig 		    unsigned int optlen)
10123fdadf7dSDmitry Mishin {
10133fdadf7dSDmitry Mishin 	int err;
10143fdadf7dSDmitry Mishin 
10153fdadf7dSDmitry Mishin 	if (level == SOL_IP && sk->sk_type != SOCK_RAW)
10163fdadf7dSDmitry Mishin 		return udp_prot.setsockopt(sk, level, optname, optval, optlen);
10173fdadf7dSDmitry Mishin 
10183fdadf7dSDmitry Mishin 	if (level != SOL_IPV6)
10193fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
10203fdadf7dSDmitry Mishin 
1021a7b75c5aSChristoph Hellwig 	err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
10223fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
10233fdadf7dSDmitry Mishin 	/* we need to exclude all possible ENOPROTOOPTs except default case */
10243fdadf7dSDmitry Mishin 	if (err == -ENOPROTOOPT && optname != IPV6_IPSEC_POLICY &&
10253f34cfaeSPaolo Abeni 			optname != IPV6_XFRM_POLICY)
1026a7b75c5aSChristoph Hellwig 		err = nf_setsockopt(sk, PF_INET6, optname, optval, optlen);
10273fdadf7dSDmitry Mishin #endif
10283fdadf7dSDmitry Mishin 	return err;
10293fdadf7dSDmitry Mishin }
10307159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ipv6_setsockopt);
10313fdadf7dSDmitry Mishin 
103228693079SDavid S. Miller static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
10334c6510a7SYOSHIFUJI Hideaki 				  int optname, char __user *optval, int len)
1034333fad53SYOSHIFUJI Hideaki {
103528693079SDavid S. Miller 	struct ipv6_opt_hdr *hdr;
103628693079SDavid S. Miller 
10374c6510a7SYOSHIFUJI Hideaki 	if (!opt)
1038333fad53SYOSHIFUJI Hideaki 		return 0;
10394c6510a7SYOSHIFUJI Hideaki 
10404c6510a7SYOSHIFUJI Hideaki 	switch (optname) {
10414c6510a7SYOSHIFUJI Hideaki 	case IPV6_HOPOPTS:
104228693079SDavid S. Miller 		hdr = opt->hopopt;
10434c6510a7SYOSHIFUJI Hideaki 		break;
10444c6510a7SYOSHIFUJI Hideaki 	case IPV6_RTHDRDSTOPTS:
10454c6510a7SYOSHIFUJI Hideaki 		hdr = opt->dst0opt;
10464c6510a7SYOSHIFUJI Hideaki 		break;
10474c6510a7SYOSHIFUJI Hideaki 	case IPV6_RTHDR:
10484c6510a7SYOSHIFUJI Hideaki 		hdr = (struct ipv6_opt_hdr *)opt->srcrt;
10494c6510a7SYOSHIFUJI Hideaki 		break;
10504c6510a7SYOSHIFUJI Hideaki 	case IPV6_DSTOPTS:
10514c6510a7SYOSHIFUJI Hideaki 		hdr = opt->dst1opt;
10524c6510a7SYOSHIFUJI Hideaki 		break;
10534c6510a7SYOSHIFUJI Hideaki 	default:
10544c6510a7SYOSHIFUJI Hideaki 		return -EINVAL;	/* should not happen */
10554c6510a7SYOSHIFUJI Hideaki 	}
10564c6510a7SYOSHIFUJI Hideaki 
10574c6510a7SYOSHIFUJI Hideaki 	if (!hdr)
10584c6510a7SYOSHIFUJI Hideaki 		return 0;
105928693079SDavid S. Miller 
1060d2b02ed9SChris Wright 	len = min_t(unsigned int, len, ipv6_optlen(hdr));
1061660adc6eSIlpo Järvinen 	if (copy_to_user(optval, hdr, len))
1062333fad53SYOSHIFUJI Hideaki 		return -EFAULT;
106395b496b6SYang Hongyang 	return len;
1064333fad53SYOSHIFUJI Hideaki }
1065333fad53SYOSHIFUJI Hideaki 
1066d5541e85SChristoph Hellwig static int ipv6_get_msfilter(struct sock *sk, void __user *optval,
1067d5541e85SChristoph Hellwig 		int __user *optlen, int len)
10681da177e4SLinus Torvalds {
1069db243b79SGustavo A. R. Silva 	const int size0 = offsetof(struct group_filter, gf_slist_flex);
1070d5541e85SChristoph Hellwig 	struct group_filter __user *p = optval;
1071d5541e85SChristoph Hellwig 	struct group_filter gsf;
1072931ca7abSAl Viro 	int num;
10731da177e4SLinus Torvalds 	int err;
10741da177e4SLinus Torvalds 
1075931ca7abSAl Viro 	if (len < size0)
10761da177e4SLinus Torvalds 		return -EINVAL;
1077931ca7abSAl Viro 	if (copy_from_user(&gsf, p, size0))
10781da177e4SLinus Torvalds 		return -EFAULT;
107920c61fbdSYOSHIFUJI Hideaki 	if (gsf.gf_group.ss_family != AF_INET6)
108020c61fbdSYOSHIFUJI Hideaki 		return -EADDRNOTAVAIL;
1081931ca7abSAl Viro 	num = gsf.gf_numsrc;
10821da177e4SLinus Torvalds 	lock_sock(sk);
1083db243b79SGustavo A. R. Silva 	err = ip6_mc_msfget(sk, &gsf, p->gf_slist_flex);
1084931ca7abSAl Viro 	if (!err) {
1085931ca7abSAl Viro 		if (num > gsf.gf_numsrc)
1086931ca7abSAl Viro 			num = gsf.gf_numsrc;
1087931ca7abSAl Viro 		if (put_user(GROUP_FILTER_SIZE(num), optlen) ||
1088931ca7abSAl Viro 		    copy_to_user(p, &gsf, size0))
1089931ca7abSAl Viro 			err = -EFAULT;
1090931ca7abSAl Viro 	}
10911da177e4SLinus Torvalds 	release_sock(sk);
10921da177e4SLinus Torvalds 	return err;
10931da177e4SLinus Torvalds }
10941da177e4SLinus Torvalds 
1095d5541e85SChristoph Hellwig static int compat_ipv6_get_msfilter(struct sock *sk, void __user *optval,
1096d5541e85SChristoph Hellwig 		int __user *optlen)
1097d5541e85SChristoph Hellwig {
1098db243b79SGustavo A. R. Silva 	const int size0 = offsetof(struct compat_group_filter, gf_slist_flex);
1099d5541e85SChristoph Hellwig 	struct compat_group_filter __user *p = optval;
1100d5541e85SChristoph Hellwig 	struct compat_group_filter gf32;
1101d5541e85SChristoph Hellwig 	struct group_filter gf;
1102d5541e85SChristoph Hellwig 	int len, err;
1103d5541e85SChristoph Hellwig 	int num;
1104d5541e85SChristoph Hellwig 
1105d5541e85SChristoph Hellwig 	if (get_user(len, optlen))
1106d5541e85SChristoph Hellwig 		return -EFAULT;
1107d5541e85SChristoph Hellwig 	if (len < size0)
1108d5541e85SChristoph Hellwig 		return -EINVAL;
1109d5541e85SChristoph Hellwig 
1110d5541e85SChristoph Hellwig 	if (copy_from_user(&gf32, p, size0))
1111d5541e85SChristoph Hellwig 		return -EFAULT;
1112d5541e85SChristoph Hellwig 	gf.gf_interface = gf32.gf_interface;
1113d5541e85SChristoph Hellwig 	gf.gf_fmode = gf32.gf_fmode;
1114d5541e85SChristoph Hellwig 	num = gf.gf_numsrc = gf32.gf_numsrc;
1115d5541e85SChristoph Hellwig 	gf.gf_group = gf32.gf_group;
1116d5541e85SChristoph Hellwig 
1117d5541e85SChristoph Hellwig 	if (gf.gf_group.ss_family != AF_INET6)
1118d5541e85SChristoph Hellwig 		return -EADDRNOTAVAIL;
1119d5541e85SChristoph Hellwig 
1120d5541e85SChristoph Hellwig 	lock_sock(sk);
1121db243b79SGustavo A. R. Silva 	err = ip6_mc_msfget(sk, &gf, p->gf_slist_flex);
1122d5541e85SChristoph Hellwig 	release_sock(sk);
1123d5541e85SChristoph Hellwig 	if (err)
1124d5541e85SChristoph Hellwig 		return err;
1125d5541e85SChristoph Hellwig 	if (num > gf.gf_numsrc)
1126d5541e85SChristoph Hellwig 		num = gf.gf_numsrc;
1127d5541e85SChristoph Hellwig 	len = GROUP_FILTER_SIZE(num) - (sizeof(gf)-sizeof(gf32));
1128d5541e85SChristoph Hellwig 	if (put_user(len, optlen) ||
1129d5541e85SChristoph Hellwig 	    put_user(gf.gf_fmode, &p->gf_fmode) ||
1130d5541e85SChristoph Hellwig 	    put_user(gf.gf_numsrc, &p->gf_numsrc))
1131d5541e85SChristoph Hellwig 		return -EFAULT;
1132d5541e85SChristoph Hellwig 	return 0;
1133d5541e85SChristoph Hellwig }
1134d5541e85SChristoph Hellwig 
1135d5541e85SChristoph Hellwig static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
1136d5541e85SChristoph Hellwig 		    char __user *optval, int __user *optlen, unsigned int flags)
1137d5541e85SChristoph Hellwig {
1138d5541e85SChristoph Hellwig 	struct ipv6_pinfo *np = inet6_sk(sk);
1139d5541e85SChristoph Hellwig 	int len;
1140d5541e85SChristoph Hellwig 	int val;
1141d5541e85SChristoph Hellwig 
1142d5541e85SChristoph Hellwig 	if (ip6_mroute_opt(optname))
1143d5541e85SChristoph Hellwig 		return ip6_mroute_getsockopt(sk, optname, optval, optlen);
1144d5541e85SChristoph Hellwig 
1145d5541e85SChristoph Hellwig 	if (get_user(len, optlen))
1146d5541e85SChristoph Hellwig 		return -EFAULT;
1147d5541e85SChristoph Hellwig 	switch (optname) {
1148d5541e85SChristoph Hellwig 	case IPV6_ADDRFORM:
1149d5541e85SChristoph Hellwig 		if (sk->sk_protocol != IPPROTO_UDP &&
1150d5541e85SChristoph Hellwig 		    sk->sk_protocol != IPPROTO_UDPLITE &&
1151d5541e85SChristoph Hellwig 		    sk->sk_protocol != IPPROTO_TCP)
1152d5541e85SChristoph Hellwig 			return -ENOPROTOOPT;
1153d5541e85SChristoph Hellwig 		if (sk->sk_state != TCP_ESTABLISHED)
1154d5541e85SChristoph Hellwig 			return -ENOTCONN;
1155d5541e85SChristoph Hellwig 		val = sk->sk_family;
1156d5541e85SChristoph Hellwig 		break;
1157d5541e85SChristoph Hellwig 	case MCAST_MSFILTER:
11583021ad52SChristoph Hellwig 		if (in_compat_syscall())
11593021ad52SChristoph Hellwig 			return compat_ipv6_get_msfilter(sk, optval, optlen);
1160d5541e85SChristoph Hellwig 		return ipv6_get_msfilter(sk, optval, optlen, len);
1161333fad53SYOSHIFUJI Hideaki 	case IPV6_2292PKTOPTIONS:
11621da177e4SLinus Torvalds 	{
11631da177e4SLinus Torvalds 		struct msghdr msg;
11641da177e4SLinus Torvalds 		struct sk_buff *skb;
11651da177e4SLinus Torvalds 
11661da177e4SLinus Torvalds 		if (sk->sk_type != SOCK_STREAM)
11671da177e4SLinus Torvalds 			return -ENOPROTOOPT;
11681da177e4SLinus Torvalds 
11699e39394fSLukas Bulwahn 		msg.msg_control_user = optval;
11701da177e4SLinus Torvalds 		msg.msg_controllen = len;
117198e77438SDaniel Baluta 		msg.msg_flags = flags;
11721b2f08dfSChristoph Hellwig 		msg.msg_control_is_user = true;
11731da177e4SLinus Torvalds 
11741da177e4SLinus Torvalds 		lock_sock(sk);
11751da177e4SLinus Torvalds 		skb = np->pktoptions;
11761da177e4SLinus Torvalds 		if (skb)
11774b261c75SHannes Frederic Sowa 			ip6_datagram_recv_ctl(sk, &msg, skb);
11781dc7b90fSEric Dumazet 		release_sock(sk);
11791dc7b90fSEric Dumazet 		if (!skb) {
11801da177e4SLinus Torvalds 			if (np->rxopt.bits.rxinfo) {
11811da177e4SLinus Torvalds 				struct in6_pktinfo src_info;
1182f250dcdaSYang Hongyang 				src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
1183f250dcdaSYang Hongyang 					np->sticky_pktinfo.ipi6_ifindex;
1184efe4208fSEric Dumazet 				src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr : np->sticky_pktinfo.ipi6_addr;
11851da177e4SLinus Torvalds 				put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
11861da177e4SLinus Torvalds 			}
11871da177e4SLinus Torvalds 			if (np->rxopt.bits.rxhlim) {
11881da177e4SLinus Torvalds 				int hlim = np->mcast_hops;
11891da177e4SLinus Torvalds 				put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
11901da177e4SLinus Torvalds 			}
11914c507d28SJiri Benc 			if (np->rxopt.bits.rxtclass) {
1192d76ed22bSLi RongQing 				int tclass = (int)ip6_tclass(np->rcv_flowinfo);
1193d76ed22bSLi RongQing 
11944c507d28SJiri Benc 				put_cmsg(&msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
11954c507d28SJiri Benc 			}
1196333fad53SYOSHIFUJI Hideaki 			if (np->rxopt.bits.rxoinfo) {
1197333fad53SYOSHIFUJI Hideaki 				struct in6_pktinfo src_info;
1198f250dcdaSYang Hongyang 				src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
1199f250dcdaSYang Hongyang 					np->sticky_pktinfo.ipi6_ifindex;
1200efe4208fSEric Dumazet 				src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr :
1201efe4208fSEric Dumazet 								     np->sticky_pktinfo.ipi6_addr;
1202333fad53SYOSHIFUJI Hideaki 				put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
1203333fad53SYOSHIFUJI Hideaki 			}
1204333fad53SYOSHIFUJI Hideaki 			if (np->rxopt.bits.rxohlim) {
1205333fad53SYOSHIFUJI Hideaki 				int hlim = np->mcast_hops;
1206333fad53SYOSHIFUJI Hideaki 				put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
1207333fad53SYOSHIFUJI Hideaki 			}
12081397ed35SFlorent Fourcot 			if (np->rxopt.bits.rxflow) {
120968536053SFlorent Fourcot 				__be32 flowinfo = np->rcv_flowinfo;
121068536053SFlorent Fourcot 
12111397ed35SFlorent Fourcot 				put_cmsg(&msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
12121397ed35SFlorent Fourcot 			}
12131da177e4SLinus Torvalds 		}
12141da177e4SLinus Torvalds 		len -= msg.msg_controllen;
12151da177e4SLinus Torvalds 		return put_user(len, optlen);
12161da177e4SLinus Torvalds 	}
12171da177e4SLinus Torvalds 	case IPV6_MTU:
12181da177e4SLinus Torvalds 	{
12191da177e4SLinus Torvalds 		struct dst_entry *dst;
1220b6c6712aSEric Dumazet 
12211da177e4SLinus Torvalds 		val = 0;
1222b6c6712aSEric Dumazet 		rcu_read_lock();
1223b6c6712aSEric Dumazet 		dst = __sk_dst_get(sk);
1224b6c6712aSEric Dumazet 		if (dst)
12251da177e4SLinus Torvalds 			val = dst_mtu(dst);
1226b6c6712aSEric Dumazet 		rcu_read_unlock();
12271da177e4SLinus Torvalds 		if (!val)
12281da177e4SLinus Torvalds 			return -ENOTCONN;
12291da177e4SLinus Torvalds 		break;
12301da177e4SLinus Torvalds 	}
12311da177e4SLinus Torvalds 
12321da177e4SLinus Torvalds 	case IPV6_V6ONLY:
12339fe516baSEric Dumazet 		val = sk->sk_ipv6only;
12341da177e4SLinus Torvalds 		break;
12351da177e4SLinus Torvalds 
1236333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVPKTINFO:
12371da177e4SLinus Torvalds 		val = np->rxopt.bits.rxinfo;
12381da177e4SLinus Torvalds 		break;
12391da177e4SLinus Torvalds 
1240333fad53SYOSHIFUJI Hideaki 	case IPV6_2292PKTINFO:
1241333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.rxoinfo;
1242333fad53SYOSHIFUJI Hideaki 		break;
1243333fad53SYOSHIFUJI Hideaki 
1244333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVHOPLIMIT:
12451da177e4SLinus Torvalds 		val = np->rxopt.bits.rxhlim;
12461da177e4SLinus Torvalds 		break;
12471da177e4SLinus Torvalds 
1248333fad53SYOSHIFUJI Hideaki 	case IPV6_2292HOPLIMIT:
1249333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.rxohlim;
1250333fad53SYOSHIFUJI Hideaki 		break;
1251333fad53SYOSHIFUJI Hideaki 
1252333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVRTHDR:
12531da177e4SLinus Torvalds 		val = np->rxopt.bits.srcrt;
12541da177e4SLinus Torvalds 		break;
12551da177e4SLinus Torvalds 
1256333fad53SYOSHIFUJI Hideaki 	case IPV6_2292RTHDR:
1257333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.osrcrt;
1258333fad53SYOSHIFUJI Hideaki 		break;
1259333fad53SYOSHIFUJI Hideaki 
12601da177e4SLinus Torvalds 	case IPV6_HOPOPTS:
1261333fad53SYOSHIFUJI Hideaki 	case IPV6_RTHDRDSTOPTS:
1262333fad53SYOSHIFUJI Hideaki 	case IPV6_RTHDR:
1263333fad53SYOSHIFUJI Hideaki 	case IPV6_DSTOPTS:
1264333fad53SYOSHIFUJI Hideaki 	{
126545f6fad8SEric Dumazet 		struct ipv6_txoptions *opt;
1266333fad53SYOSHIFUJI Hideaki 
1267333fad53SYOSHIFUJI Hideaki 		lock_sock(sk);
12681e1d04e6SHannes Frederic Sowa 		opt = rcu_dereference_protected(np->opt,
12691e1d04e6SHannes Frederic Sowa 						lockdep_sock_is_held(sk));
127045f6fad8SEric Dumazet 		len = ipv6_getsockopt_sticky(sk, opt, optname, optval, len);
1271333fad53SYOSHIFUJI Hideaki 		release_sock(sk);
127205335c22SYang Hongyang 		/* check if ipv6_getsockopt_sticky() returns err code */
127305335c22SYang Hongyang 		if (len < 0)
127405335c22SYang Hongyang 			return len;
1275333fad53SYOSHIFUJI Hideaki 		return put_user(len, optlen);
1276333fad53SYOSHIFUJI Hideaki 	}
1277333fad53SYOSHIFUJI Hideaki 
1278333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVHOPOPTS:
12791da177e4SLinus Torvalds 		val = np->rxopt.bits.hopopts;
12801da177e4SLinus Torvalds 		break;
12811da177e4SLinus Torvalds 
1282333fad53SYOSHIFUJI Hideaki 	case IPV6_2292HOPOPTS:
1283333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.ohopopts;
1284333fad53SYOSHIFUJI Hideaki 		break;
1285333fad53SYOSHIFUJI Hideaki 
1286333fad53SYOSHIFUJI Hideaki 	case IPV6_RECVDSTOPTS:
12871da177e4SLinus Torvalds 		val = np->rxopt.bits.dstopts;
12881da177e4SLinus Torvalds 		break;
12891da177e4SLinus Torvalds 
1290333fad53SYOSHIFUJI Hideaki 	case IPV6_2292DSTOPTS:
1291333fad53SYOSHIFUJI Hideaki 		val = np->rxopt.bits.odstopts;
1292333fad53SYOSHIFUJI Hideaki 		break;
1293333fad53SYOSHIFUJI Hideaki 
129441a1f8eaSYOSHIFUJI Hideaki 	case IPV6_TCLASS:
129541a1f8eaSYOSHIFUJI Hideaki 		val = np->tclass;
129641a1f8eaSYOSHIFUJI Hideaki 		break;
129741a1f8eaSYOSHIFUJI Hideaki 
129841a1f8eaSYOSHIFUJI Hideaki 	case IPV6_RECVTCLASS:
129941a1f8eaSYOSHIFUJI Hideaki 		val = np->rxopt.bits.rxtclass;
130041a1f8eaSYOSHIFUJI Hideaki 		break;
130141a1f8eaSYOSHIFUJI Hideaki 
13021da177e4SLinus Torvalds 	case IPV6_FLOWINFO:
13031da177e4SLinus Torvalds 		val = np->rxopt.bits.rxflow;
13041da177e4SLinus Torvalds 		break;
13051da177e4SLinus Torvalds 
1306793b1473SBrian Haley 	case IPV6_RECVPATHMTU:
1307793b1473SBrian Haley 		val = np->rxopt.bits.rxpmtu;
1308793b1473SBrian Haley 		break;
1309793b1473SBrian Haley 
1310793b1473SBrian Haley 	case IPV6_PATHMTU:
1311793b1473SBrian Haley 	{
1312793b1473SBrian Haley 		struct dst_entry *dst;
1313793b1473SBrian Haley 		struct ip6_mtuinfo mtuinfo;
1314793b1473SBrian Haley 
1315793b1473SBrian Haley 		if (len < sizeof(mtuinfo))
1316793b1473SBrian Haley 			return -EINVAL;
1317793b1473SBrian Haley 
1318793b1473SBrian Haley 		len = sizeof(mtuinfo);
1319793b1473SBrian Haley 		memset(&mtuinfo, 0, sizeof(mtuinfo));
1320793b1473SBrian Haley 
1321793b1473SBrian Haley 		rcu_read_lock();
1322793b1473SBrian Haley 		dst = __sk_dst_get(sk);
1323793b1473SBrian Haley 		if (dst)
1324793b1473SBrian Haley 			mtuinfo.ip6m_mtu = dst_mtu(dst);
1325793b1473SBrian Haley 		rcu_read_unlock();
1326793b1473SBrian Haley 		if (!mtuinfo.ip6m_mtu)
1327793b1473SBrian Haley 			return -ENOTCONN;
1328793b1473SBrian Haley 
1329793b1473SBrian Haley 		if (put_user(len, optlen))
1330793b1473SBrian Haley 			return -EFAULT;
1331793b1473SBrian Haley 		if (copy_to_user(optval, &mtuinfo, len))
1332793b1473SBrian Haley 			return -EFAULT;
1333793b1473SBrian Haley 
1334793b1473SBrian Haley 		return 0;
1335793b1473SBrian Haley 	}
1336793b1473SBrian Haley 
13376c468622SBalazs Scheidler 	case IPV6_TRANSPARENT:
13386c468622SBalazs Scheidler 		val = inet_sk(sk)->transparent;
13396c468622SBalazs Scheidler 		break;
13406c468622SBalazs Scheidler 
134184e14fe3SMaciej Żenczykowski 	case IPV6_FREEBIND:
134284e14fe3SMaciej Żenczykowski 		val = inet_sk(sk)->freebind;
134384e14fe3SMaciej Żenczykowski 		break;
134484e14fe3SMaciej Żenczykowski 
13456c468622SBalazs Scheidler 	case IPV6_RECVORIGDSTADDR:
13466c468622SBalazs Scheidler 		val = np->rxopt.bits.rxorigdstaddr;
13476c468622SBalazs Scheidler 		break;
13486c468622SBalazs Scheidler 
13491da177e4SLinus Torvalds 	case IPV6_UNICAST_HOPS:
13501da177e4SLinus Torvalds 	case IPV6_MULTICAST_HOPS:
1351befffe90SBrian Haley 	{
1352befffe90SBrian Haley 		struct dst_entry *dst;
1353befffe90SBrian Haley 
1354befffe90SBrian Haley 		if (optname == IPV6_UNICAST_HOPS)
1355befffe90SBrian Haley 			val = np->hop_limit;
1356befffe90SBrian Haley 		else
13571da177e4SLinus Torvalds 			val = np->mcast_hops;
1358befffe90SBrian Haley 
1359b6c6712aSEric Dumazet 		if (val < 0) {
1360b6c6712aSEric Dumazet 			rcu_read_lock();
1361b6c6712aSEric Dumazet 			dst = __sk_dst_get(sk);
1362b6c6712aSEric Dumazet 			if (dst)
13636b75d090SYOSHIFUJI Hideaki 				val = ip6_dst_hoplimit(dst);
1364b6c6712aSEric Dumazet 			rcu_read_unlock();
1365befffe90SBrian Haley 		}
1366b6c6712aSEric Dumazet 
1367befffe90SBrian Haley 		if (val < 0)
136853b7997fSYOSHIFUJI Hideaki 			val = sock_net(sk)->ipv6.devconf_all->hop_limit;
13691da177e4SLinus Torvalds 		break;
1370befffe90SBrian Haley 	}
13711da177e4SLinus Torvalds 
13721da177e4SLinus Torvalds 	case IPV6_MULTICAST_LOOP:
13731da177e4SLinus Torvalds 		val = np->mc_loop;
13741da177e4SLinus Torvalds 		break;
13751da177e4SLinus Torvalds 
13761da177e4SLinus Torvalds 	case IPV6_MULTICAST_IF:
13771da177e4SLinus Torvalds 		val = np->mcast_oif;
13781da177e4SLinus Torvalds 		break;
13791da177e4SLinus Torvalds 
138015033f04SAndre Naujoks 	case IPV6_MULTICAST_ALL:
138115033f04SAndre Naujoks 		val = np->mc_all;
138215033f04SAndre Naujoks 		break;
138315033f04SAndre Naujoks 
1384c4062dfcSErich E. Hoover 	case IPV6_UNICAST_IF:
1385c4062dfcSErich E. Hoover 		val = (__force int)htonl((__u32) np->ucast_oif);
1386c4062dfcSErich E. Hoover 		break;
1387c4062dfcSErich E. Hoover 
13881da177e4SLinus Torvalds 	case IPV6_MTU_DISCOVER:
13891da177e4SLinus Torvalds 		val = np->pmtudisc;
13901da177e4SLinus Torvalds 		break;
13911da177e4SLinus Torvalds 
13921da177e4SLinus Torvalds 	case IPV6_RECVERR:
13931da177e4SLinus Torvalds 		val = np->recverr;
13941da177e4SLinus Torvalds 		break;
13951da177e4SLinus Torvalds 
13961da177e4SLinus Torvalds 	case IPV6_FLOWINFO_SEND:
13971da177e4SLinus Torvalds 		val = np->sndflow;
13981da177e4SLinus Torvalds 		break;
13991da177e4SLinus Torvalds 
14003fdfa5ffSFlorent Fourcot 	case IPV6_FLOWLABEL_MGR:
14013fdfa5ffSFlorent Fourcot 	{
14023fdfa5ffSFlorent Fourcot 		struct in6_flowlabel_req freq;
140346e5f401SFlorent Fourcot 		int flags;
14043fdfa5ffSFlorent Fourcot 
14053fdfa5ffSFlorent Fourcot 		if (len < sizeof(freq))
14063fdfa5ffSFlorent Fourcot 			return -EINVAL;
14073fdfa5ffSFlorent Fourcot 
14083fdfa5ffSFlorent Fourcot 		if (copy_from_user(&freq, optval, sizeof(freq)))
14093fdfa5ffSFlorent Fourcot 			return -EFAULT;
14103fdfa5ffSFlorent Fourcot 
14113fdfa5ffSFlorent Fourcot 		if (freq.flr_action != IPV6_FL_A_GET)
14123fdfa5ffSFlorent Fourcot 			return -EINVAL;
14133fdfa5ffSFlorent Fourcot 
14143fdfa5ffSFlorent Fourcot 		len = sizeof(freq);
141546e5f401SFlorent Fourcot 		flags = freq.flr_flags;
141646e5f401SFlorent Fourcot 
14173fdfa5ffSFlorent Fourcot 		memset(&freq, 0, sizeof(freq));
14183fdfa5ffSFlorent Fourcot 
141946e5f401SFlorent Fourcot 		val = ipv6_flowlabel_opt_get(sk, &freq, flags);
14203fdfa5ffSFlorent Fourcot 		if (val < 0)
14213fdfa5ffSFlorent Fourcot 			return val;
14223fdfa5ffSFlorent Fourcot 
14233fdfa5ffSFlorent Fourcot 		if (put_user(len, optlen))
14243fdfa5ffSFlorent Fourcot 			return -EFAULT;
14253fdfa5ffSFlorent Fourcot 		if (copy_to_user(optval, &freq, len))
14263fdfa5ffSFlorent Fourcot 			return -EFAULT;
14273fdfa5ffSFlorent Fourcot 
14283fdfa5ffSFlorent Fourcot 		return 0;
14293fdfa5ffSFlorent Fourcot 	}
14303fdfa5ffSFlorent Fourcot 
14317cbca67cSYOSHIFUJI Hideaki 	case IPV6_ADDR_PREFERENCES:
14327cbca67cSYOSHIFUJI Hideaki 		val = 0;
14337cbca67cSYOSHIFUJI Hideaki 
14347cbca67cSYOSHIFUJI Hideaki 		if (np->srcprefs & IPV6_PREFER_SRC_TMP)
14357cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_TMP;
14367cbca67cSYOSHIFUJI Hideaki 		else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC)
14377cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_PUBLIC;
14387cbca67cSYOSHIFUJI Hideaki 		else {
14397cbca67cSYOSHIFUJI Hideaki 			/* XXX: should we return system default? */
14407cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT;
14417cbca67cSYOSHIFUJI Hideaki 		}
14427cbca67cSYOSHIFUJI Hideaki 
14437cbca67cSYOSHIFUJI Hideaki 		if (np->srcprefs & IPV6_PREFER_SRC_COA)
14447cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_COA;
14457cbca67cSYOSHIFUJI Hideaki 		else
14467cbca67cSYOSHIFUJI Hideaki 			val |= IPV6_PREFER_SRC_HOME;
14477cbca67cSYOSHIFUJI Hideaki 		break;
14487cbca67cSYOSHIFUJI Hideaki 
1449e802af9cSStephen Hemminger 	case IPV6_MINHOPCOUNT:
1450e802af9cSStephen Hemminger 		val = np->min_hopcount;
1451e802af9cSStephen Hemminger 		break;
1452e802af9cSStephen Hemminger 
1453793b1473SBrian Haley 	case IPV6_DONTFRAG:
1454793b1473SBrian Haley 		val = np->dontfrag;
1455793b1473SBrian Haley 		break;
1456793b1473SBrian Haley 
1457cb1ce2efSTom Herbert 	case IPV6_AUTOFLOWLABEL:
1458e9191ffbSBen Hutchings 		val = ip6_autoflowlabel(sock_net(sk), np);
1459cb1ce2efSTom Herbert 		break;
1460cb1ce2efSTom Herbert 
14610cc0aa61SWillem de Bruijn 	case IPV6_RECVFRAGSIZE:
14620cc0aa61SWillem de Bruijn 		val = np->rxopt.bits.recvfragsize;
14630cc0aa61SWillem de Bruijn 		break;
14640cc0aa61SWillem de Bruijn 
14659036b2feSFrancesco Ruggeri 	case IPV6_ROUTER_ALERT_ISOLATE:
14669036b2feSFrancesco Ruggeri 		val = np->rtalert_isolate;
14679036b2feSFrancesco Ruggeri 		break;
14689036b2feSFrancesco Ruggeri 
146901370434SWillem de Bruijn 	case IPV6_RECVERR_RFC4884:
147001370434SWillem de Bruijn 		val = np->recverr_rfc4884;
147101370434SWillem de Bruijn 		break;
147201370434SWillem de Bruijn 
14731da177e4SLinus Torvalds 	default:
1474cf6fc4a9SWei Yongjun 		return -ENOPROTOOPT;
14751da177e4SLinus Torvalds 	}
14761da177e4SLinus Torvalds 	len = min_t(unsigned int, sizeof(int), len);
14771da177e4SLinus Torvalds 	if (put_user(len, optlen))
14781da177e4SLinus Torvalds 		return -EFAULT;
14791da177e4SLinus Torvalds 	if (copy_to_user(optval, &val, len))
14801da177e4SLinus Torvalds 		return -EFAULT;
14811da177e4SLinus Torvalds 	return 0;
14821da177e4SLinus Torvalds }
14831da177e4SLinus Torvalds 
14843fdadf7dSDmitry Mishin int ipv6_getsockopt(struct sock *sk, int level, int optname,
14853fdadf7dSDmitry Mishin 		    char __user *optval, int __user *optlen)
14863fdadf7dSDmitry Mishin {
14873fdadf7dSDmitry Mishin 	int err;
14883fdadf7dSDmitry Mishin 
14893fdadf7dSDmitry Mishin 	if (level == SOL_IP && sk->sk_type != SOCK_RAW)
14903fdadf7dSDmitry Mishin 		return udp_prot.getsockopt(sk, level, optname, optval, optlen);
14913fdadf7dSDmitry Mishin 
14923fdadf7dSDmitry Mishin 	if (level != SOL_IPV6)
14933fdadf7dSDmitry Mishin 		return -ENOPROTOOPT;
14943fdadf7dSDmitry Mishin 
149598e77438SDaniel Baluta 	err = do_ipv6_getsockopt(sk, level, optname, optval, optlen, 0);
14963fdadf7dSDmitry Mishin #ifdef CONFIG_NETFILTER
1497cf6fc4a9SWei Yongjun 	/* we need to exclude all possible ENOPROTOOPTs except default case */
1498cf6fc4a9SWei Yongjun 	if (err == -ENOPROTOOPT && optname != IPV6_2292PKTOPTIONS) {
14993fdadf7dSDmitry Mishin 		int len;
15003fdadf7dSDmitry Mishin 
15013fdadf7dSDmitry Mishin 		if (get_user(len, optlen))
15023fdadf7dSDmitry Mishin 			return -EFAULT;
15033fdadf7dSDmitry Mishin 
150401ea306fSPaolo Abeni 		err = nf_getsockopt(sk, PF_INET6, optname, optval, &len);
15053fdadf7dSDmitry Mishin 		if (err >= 0)
15063fdadf7dSDmitry Mishin 			err = put_user(len, optlen);
15073fdadf7dSDmitry Mishin 	}
15083fdadf7dSDmitry Mishin #endif
15093fdadf7dSDmitry Mishin 	return err;
15103fdadf7dSDmitry Mishin }
15117159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ipv6_getsockopt);
1512