162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	IPv6 BSD socket options interface
462306a36Sopenharmony_ci *	Linux INET6 implementation
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *	Authors:
762306a36Sopenharmony_ci *	Pedro Roque		<roque@di.fc.ul.pt>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *	Based on linux/net/ipv4/ip_sockglue.c
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *	FIXME: Make the setsockopt code POSIX compliant: That is
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci *	o	Truncate getsockopt returns
1462306a36Sopenharmony_ci *	o	Return an optlen of the truncated length if need be
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *	Changes:
1762306a36Sopenharmony_ci *	David L Stevens <dlstevens@us.ibm.com>:
1862306a36Sopenharmony_ci *		- added multicast source filtering API for MLDv2
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/module.h>
2262306a36Sopenharmony_ci#include <linux/capability.h>
2362306a36Sopenharmony_ci#include <linux/errno.h>
2462306a36Sopenharmony_ci#include <linux/types.h>
2562306a36Sopenharmony_ci#include <linux/socket.h>
2662306a36Sopenharmony_ci#include <linux/sockios.h>
2762306a36Sopenharmony_ci#include <linux/net.h>
2862306a36Sopenharmony_ci#include <linux/in6.h>
2962306a36Sopenharmony_ci#include <linux/mroute6.h>
3062306a36Sopenharmony_ci#include <linux/netdevice.h>
3162306a36Sopenharmony_ci#include <linux/if_arp.h>
3262306a36Sopenharmony_ci#include <linux/init.h>
3362306a36Sopenharmony_ci#include <linux/sysctl.h>
3462306a36Sopenharmony_ci#include <linux/netfilter.h>
3562306a36Sopenharmony_ci#include <linux/slab.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include <net/sock.h>
3862306a36Sopenharmony_ci#include <net/snmp.h>
3962306a36Sopenharmony_ci#include <net/ipv6.h>
4062306a36Sopenharmony_ci#include <net/ndisc.h>
4162306a36Sopenharmony_ci#include <net/protocol.h>
4262306a36Sopenharmony_ci#include <net/transp_v6.h>
4362306a36Sopenharmony_ci#include <net/ip6_route.h>
4462306a36Sopenharmony_ci#include <net/addrconf.h>
4562306a36Sopenharmony_ci#include <net/inet_common.h>
4662306a36Sopenharmony_ci#include <net/tcp.h>
4762306a36Sopenharmony_ci#include <net/udp.h>
4862306a36Sopenharmony_ci#include <net/udplite.h>
4962306a36Sopenharmony_ci#include <net/xfrm.h>
5062306a36Sopenharmony_ci#include <net/compat.h>
5162306a36Sopenharmony_ci#include <net/seg6.h>
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#include <linux/uaccess.h>
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistruct ip6_ra_chain *ip6_ra_chain;
5662306a36Sopenharmony_ciDEFINE_RWLOCK(ip6_ra_lock);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(ip6_min_hopcount);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciint ip6_ra_control(struct sock *sk, int sel)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct ip6_ra_chain *ra, *new_ra, **rap;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* RA packet may be delivered ONLY to IPPROTO_RAW socket */
6562306a36Sopenharmony_ci	if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num != IPPROTO_RAW)
6662306a36Sopenharmony_ci		return -ENOPROTOOPT;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	new_ra = (sel >= 0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
6962306a36Sopenharmony_ci	if (sel >= 0 && !new_ra)
7062306a36Sopenharmony_ci		return -ENOMEM;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	write_lock_bh(&ip6_ra_lock);
7362306a36Sopenharmony_ci	for (rap = &ip6_ra_chain; (ra = *rap) != NULL; rap = &ra->next) {
7462306a36Sopenharmony_ci		if (ra->sk == sk) {
7562306a36Sopenharmony_ci			if (sel >= 0) {
7662306a36Sopenharmony_ci				write_unlock_bh(&ip6_ra_lock);
7762306a36Sopenharmony_ci				kfree(new_ra);
7862306a36Sopenharmony_ci				return -EADDRINUSE;
7962306a36Sopenharmony_ci			}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci			*rap = ra->next;
8262306a36Sopenharmony_ci			write_unlock_bh(&ip6_ra_lock);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci			sock_put(sk);
8562306a36Sopenharmony_ci			kfree(ra);
8662306a36Sopenharmony_ci			return 0;
8762306a36Sopenharmony_ci		}
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	if (!new_ra) {
9062306a36Sopenharmony_ci		write_unlock_bh(&ip6_ra_lock);
9162306a36Sopenharmony_ci		return -ENOBUFS;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci	new_ra->sk = sk;
9462306a36Sopenharmony_ci	new_ra->sel = sel;
9562306a36Sopenharmony_ci	new_ra->next = ra;
9662306a36Sopenharmony_ci	*rap = new_ra;
9762306a36Sopenharmony_ci	sock_hold(sk);
9862306a36Sopenharmony_ci	write_unlock_bh(&ip6_ra_lock);
9962306a36Sopenharmony_ci	return 0;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistruct ipv6_txoptions *ipv6_update_options(struct sock *sk,
10362306a36Sopenharmony_ci					   struct ipv6_txoptions *opt)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	if (inet_test_bit(IS_ICSK, sk)) {
10662306a36Sopenharmony_ci		if (opt &&
10762306a36Sopenharmony_ci		    !((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) &&
10862306a36Sopenharmony_ci		    inet_sk(sk)->inet_daddr != LOOPBACK4_IPV6) {
10962306a36Sopenharmony_ci			struct inet_connection_sock *icsk = inet_csk(sk);
11062306a36Sopenharmony_ci			icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen;
11162306a36Sopenharmony_ci			icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
11262306a36Sopenharmony_ci		}
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci	opt = xchg((__force struct ipv6_txoptions **)&inet6_sk(sk)->opt,
11562306a36Sopenharmony_ci		   opt);
11662306a36Sopenharmony_ci	sk_dst_reset(sk);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	return opt;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic bool setsockopt_needs_rtnl(int optname)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	switch (optname) {
12462306a36Sopenharmony_ci	case IPV6_ADDRFORM:
12562306a36Sopenharmony_ci	case IPV6_ADD_MEMBERSHIP:
12662306a36Sopenharmony_ci	case IPV6_DROP_MEMBERSHIP:
12762306a36Sopenharmony_ci	case IPV6_JOIN_ANYCAST:
12862306a36Sopenharmony_ci	case IPV6_LEAVE_ANYCAST:
12962306a36Sopenharmony_ci	case MCAST_JOIN_GROUP:
13062306a36Sopenharmony_ci	case MCAST_LEAVE_GROUP:
13162306a36Sopenharmony_ci	case MCAST_JOIN_SOURCE_GROUP:
13262306a36Sopenharmony_ci	case MCAST_LEAVE_SOURCE_GROUP:
13362306a36Sopenharmony_ci	case MCAST_BLOCK_SOURCE:
13462306a36Sopenharmony_ci	case MCAST_UNBLOCK_SOURCE:
13562306a36Sopenharmony_ci	case MCAST_MSFILTER:
13662306a36Sopenharmony_ci		return true;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	return false;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int copy_group_source_from_sockptr(struct group_source_req *greqs,
14262306a36Sopenharmony_ci		sockptr_t optval, int optlen)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	if (in_compat_syscall()) {
14562306a36Sopenharmony_ci		struct compat_group_source_req gr32;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		if (optlen < sizeof(gr32))
14862306a36Sopenharmony_ci			return -EINVAL;
14962306a36Sopenharmony_ci		if (copy_from_sockptr(&gr32, optval, sizeof(gr32)))
15062306a36Sopenharmony_ci			return -EFAULT;
15162306a36Sopenharmony_ci		greqs->gsr_interface = gr32.gsr_interface;
15262306a36Sopenharmony_ci		greqs->gsr_group = gr32.gsr_group;
15362306a36Sopenharmony_ci		greqs->gsr_source = gr32.gsr_source;
15462306a36Sopenharmony_ci	} else {
15562306a36Sopenharmony_ci		if (optlen < sizeof(*greqs))
15662306a36Sopenharmony_ci			return -EINVAL;
15762306a36Sopenharmony_ci		if (copy_from_sockptr(greqs, optval, sizeof(*greqs)))
15862306a36Sopenharmony_ci			return -EFAULT;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return 0;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic int do_ipv6_mcast_group_source(struct sock *sk, int optname,
16562306a36Sopenharmony_ci		sockptr_t optval, int optlen)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct group_source_req greqs;
16862306a36Sopenharmony_ci	int omode, add;
16962306a36Sopenharmony_ci	int ret;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	ret = copy_group_source_from_sockptr(&greqs, optval, optlen);
17262306a36Sopenharmony_ci	if (ret)
17362306a36Sopenharmony_ci		return ret;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (greqs.gsr_group.ss_family != AF_INET6 ||
17662306a36Sopenharmony_ci	    greqs.gsr_source.ss_family != AF_INET6)
17762306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (optname == MCAST_BLOCK_SOURCE) {
18062306a36Sopenharmony_ci		omode = MCAST_EXCLUDE;
18162306a36Sopenharmony_ci		add = 1;
18262306a36Sopenharmony_ci	} else if (optname == MCAST_UNBLOCK_SOURCE) {
18362306a36Sopenharmony_ci		omode = MCAST_EXCLUDE;
18462306a36Sopenharmony_ci		add = 0;
18562306a36Sopenharmony_ci	} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
18662306a36Sopenharmony_ci		struct sockaddr_in6 *psin6;
18762306a36Sopenharmony_ci		int retv;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		psin6 = (struct sockaddr_in6 *)&greqs.gsr_group;
19062306a36Sopenharmony_ci		retv = ipv6_sock_mc_join_ssm(sk, greqs.gsr_interface,
19162306a36Sopenharmony_ci					     &psin6->sin6_addr,
19262306a36Sopenharmony_ci					     MCAST_INCLUDE);
19362306a36Sopenharmony_ci		/* prior join w/ different source is ok */
19462306a36Sopenharmony_ci		if (retv && retv != -EADDRINUSE)
19562306a36Sopenharmony_ci			return retv;
19662306a36Sopenharmony_ci		omode = MCAST_INCLUDE;
19762306a36Sopenharmony_ci		add = 1;
19862306a36Sopenharmony_ci	} else /* MCAST_LEAVE_SOURCE_GROUP */ {
19962306a36Sopenharmony_ci		omode = MCAST_INCLUDE;
20062306a36Sopenharmony_ci		add = 0;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	return ip6_mc_source(add, omode, sk, &greqs);
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic int ipv6_set_mcast_msfilter(struct sock *sk, sockptr_t optval,
20662306a36Sopenharmony_ci		int optlen)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct group_filter *gsf;
20962306a36Sopenharmony_ci	int ret;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (optlen < GROUP_FILTER_SIZE(0))
21262306a36Sopenharmony_ci		return -EINVAL;
21362306a36Sopenharmony_ci	if (optlen > READ_ONCE(sysctl_optmem_max))
21462306a36Sopenharmony_ci		return -ENOBUFS;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	gsf = memdup_sockptr(optval, optlen);
21762306a36Sopenharmony_ci	if (IS_ERR(gsf))
21862306a36Sopenharmony_ci		return PTR_ERR(gsf);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* numsrc >= (4G-140)/128 overflow in 32 bits */
22162306a36Sopenharmony_ci	ret = -ENOBUFS;
22262306a36Sopenharmony_ci	if (gsf->gf_numsrc >= 0x1ffffffU ||
22362306a36Sopenharmony_ci	    gsf->gf_numsrc > sysctl_mld_max_msf)
22462306a36Sopenharmony_ci		goto out_free_gsf;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	ret = -EINVAL;
22762306a36Sopenharmony_ci	if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen)
22862306a36Sopenharmony_ci		goto out_free_gsf;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	ret = ip6_mc_msfilter(sk, gsf, gsf->gf_slist_flex);
23162306a36Sopenharmony_ciout_free_gsf:
23262306a36Sopenharmony_ci	kfree(gsf);
23362306a36Sopenharmony_ci	return ret;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int compat_ipv6_set_mcast_msfilter(struct sock *sk, sockptr_t optval,
23762306a36Sopenharmony_ci		int optlen)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	const int size0 = offsetof(struct compat_group_filter, gf_slist_flex);
24062306a36Sopenharmony_ci	struct compat_group_filter *gf32;
24162306a36Sopenharmony_ci	void *p;
24262306a36Sopenharmony_ci	int ret;
24362306a36Sopenharmony_ci	int n;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (optlen < size0)
24662306a36Sopenharmony_ci		return -EINVAL;
24762306a36Sopenharmony_ci	if (optlen > READ_ONCE(sysctl_optmem_max) - 4)
24862306a36Sopenharmony_ci		return -ENOBUFS;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	p = kmalloc(optlen + 4, GFP_KERNEL);
25162306a36Sopenharmony_ci	if (!p)
25262306a36Sopenharmony_ci		return -ENOMEM;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	gf32 = p + 4; /* we want ->gf_group and ->gf_slist_flex aligned */
25562306a36Sopenharmony_ci	ret = -EFAULT;
25662306a36Sopenharmony_ci	if (copy_from_sockptr(gf32, optval, optlen))
25762306a36Sopenharmony_ci		goto out_free_p;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* numsrc >= (4G-140)/128 overflow in 32 bits */
26062306a36Sopenharmony_ci	ret = -ENOBUFS;
26162306a36Sopenharmony_ci	n = gf32->gf_numsrc;
26262306a36Sopenharmony_ci	if (n >= 0x1ffffffU || n > sysctl_mld_max_msf)
26362306a36Sopenharmony_ci		goto out_free_p;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	ret = -EINVAL;
26662306a36Sopenharmony_ci	if (offsetof(struct compat_group_filter, gf_slist_flex[n]) > optlen)
26762306a36Sopenharmony_ci		goto out_free_p;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	ret = ip6_mc_msfilter(sk, &(struct group_filter){
27062306a36Sopenharmony_ci			.gf_interface = gf32->gf_interface,
27162306a36Sopenharmony_ci			.gf_group = gf32->gf_group,
27262306a36Sopenharmony_ci			.gf_fmode = gf32->gf_fmode,
27362306a36Sopenharmony_ci			.gf_numsrc = gf32->gf_numsrc}, gf32->gf_slist_flex);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ciout_free_p:
27662306a36Sopenharmony_ci	kfree(p);
27762306a36Sopenharmony_ci	return ret;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic int ipv6_mcast_join_leave(struct sock *sk, int optname,
28162306a36Sopenharmony_ci		sockptr_t optval, int optlen)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct sockaddr_in6 *psin6;
28462306a36Sopenharmony_ci	struct group_req greq;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (optlen < sizeof(greq))
28762306a36Sopenharmony_ci		return -EINVAL;
28862306a36Sopenharmony_ci	if (copy_from_sockptr(&greq, optval, sizeof(greq)))
28962306a36Sopenharmony_ci		return -EFAULT;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (greq.gr_group.ss_family != AF_INET6)
29262306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
29362306a36Sopenharmony_ci	psin6 = (struct sockaddr_in6 *)&greq.gr_group;
29462306a36Sopenharmony_ci	if (optname == MCAST_JOIN_GROUP)
29562306a36Sopenharmony_ci		return ipv6_sock_mc_join(sk, greq.gr_interface,
29662306a36Sopenharmony_ci					 &psin6->sin6_addr);
29762306a36Sopenharmony_ci	return ipv6_sock_mc_drop(sk, greq.gr_interface, &psin6->sin6_addr);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic int compat_ipv6_mcast_join_leave(struct sock *sk, int optname,
30162306a36Sopenharmony_ci		sockptr_t optval, int optlen)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct compat_group_req gr32;
30462306a36Sopenharmony_ci	struct sockaddr_in6 *psin6;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	if (optlen < sizeof(gr32))
30762306a36Sopenharmony_ci		return -EINVAL;
30862306a36Sopenharmony_ci	if (copy_from_sockptr(&gr32, optval, sizeof(gr32)))
30962306a36Sopenharmony_ci		return -EFAULT;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (gr32.gr_group.ss_family != AF_INET6)
31262306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
31362306a36Sopenharmony_ci	psin6 = (struct sockaddr_in6 *)&gr32.gr_group;
31462306a36Sopenharmony_ci	if (optname == MCAST_JOIN_GROUP)
31562306a36Sopenharmony_ci		return ipv6_sock_mc_join(sk, gr32.gr_interface,
31662306a36Sopenharmony_ci					&psin6->sin6_addr);
31762306a36Sopenharmony_ci	return ipv6_sock_mc_drop(sk, gr32.gr_interface, &psin6->sin6_addr);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int ipv6_set_opt_hdr(struct sock *sk, int optname, sockptr_t optval,
32162306a36Sopenharmony_ci		int optlen)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct ipv6_pinfo *np = inet6_sk(sk);
32462306a36Sopenharmony_ci	struct ipv6_opt_hdr *new = NULL;
32562306a36Sopenharmony_ci	struct net *net = sock_net(sk);
32662306a36Sopenharmony_ci	struct ipv6_txoptions *opt;
32762306a36Sopenharmony_ci	int err;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/* hop-by-hop / destination options are privileged option */
33062306a36Sopenharmony_ci	if (optname != IPV6_RTHDR && !sockopt_ns_capable(net->user_ns, CAP_NET_RAW))
33162306a36Sopenharmony_ci		return -EPERM;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	/* remove any sticky options header with a zero option
33462306a36Sopenharmony_ci	 * length, per RFC3542.
33562306a36Sopenharmony_ci	 */
33662306a36Sopenharmony_ci	if (optlen > 0) {
33762306a36Sopenharmony_ci		if (sockptr_is_null(optval))
33862306a36Sopenharmony_ci			return -EINVAL;
33962306a36Sopenharmony_ci		if (optlen < sizeof(struct ipv6_opt_hdr) ||
34062306a36Sopenharmony_ci		    optlen & 0x7 ||
34162306a36Sopenharmony_ci		    optlen > 8 * 255)
34262306a36Sopenharmony_ci			return -EINVAL;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		new = memdup_sockptr(optval, optlen);
34562306a36Sopenharmony_ci		if (IS_ERR(new))
34662306a36Sopenharmony_ci			return PTR_ERR(new);
34762306a36Sopenharmony_ci		if (unlikely(ipv6_optlen(new) > optlen)) {
34862306a36Sopenharmony_ci			kfree(new);
34962306a36Sopenharmony_ci			return -EINVAL;
35062306a36Sopenharmony_ci		}
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	opt = rcu_dereference_protected(np->opt, lockdep_sock_is_held(sk));
35462306a36Sopenharmony_ci	opt = ipv6_renew_options(sk, opt, optname, new);
35562306a36Sopenharmony_ci	kfree(new);
35662306a36Sopenharmony_ci	if (IS_ERR(opt))
35762306a36Sopenharmony_ci		return PTR_ERR(opt);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	/* routing header option needs extra check */
36062306a36Sopenharmony_ci	err = -EINVAL;
36162306a36Sopenharmony_ci	if (optname == IPV6_RTHDR && opt && opt->srcrt) {
36262306a36Sopenharmony_ci		struct ipv6_rt_hdr *rthdr = opt->srcrt;
36362306a36Sopenharmony_ci		switch (rthdr->type) {
36462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6)
36562306a36Sopenharmony_ci		case IPV6_SRCRT_TYPE_2:
36662306a36Sopenharmony_ci			if (rthdr->hdrlen != 2 || rthdr->segments_left != 1)
36762306a36Sopenharmony_ci				goto sticky_done;
36862306a36Sopenharmony_ci			break;
36962306a36Sopenharmony_ci#endif
37062306a36Sopenharmony_ci		case IPV6_SRCRT_TYPE_4:
37162306a36Sopenharmony_ci		{
37262306a36Sopenharmony_ci			struct ipv6_sr_hdr *srh =
37362306a36Sopenharmony_ci				(struct ipv6_sr_hdr *)opt->srcrt;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci			if (!seg6_validate_srh(srh, optlen, false))
37662306a36Sopenharmony_ci				goto sticky_done;
37762306a36Sopenharmony_ci			break;
37862306a36Sopenharmony_ci		}
37962306a36Sopenharmony_ci		default:
38062306a36Sopenharmony_ci			goto sticky_done;
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	err = 0;
38562306a36Sopenharmony_ci	opt = ipv6_update_options(sk, opt);
38662306a36Sopenharmony_cisticky_done:
38762306a36Sopenharmony_ci	if (opt) {
38862306a36Sopenharmony_ci		atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
38962306a36Sopenharmony_ci		txopt_put(opt);
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci	return err;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ciint do_ipv6_setsockopt(struct sock *sk, int level, int optname,
39562306a36Sopenharmony_ci		       sockptr_t optval, unsigned int optlen)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct ipv6_pinfo *np = inet6_sk(sk);
39862306a36Sopenharmony_ci	struct net *net = sock_net(sk);
39962306a36Sopenharmony_ci	int val, valbool;
40062306a36Sopenharmony_ci	int retv = -ENOPROTOOPT;
40162306a36Sopenharmony_ci	bool needs_rtnl = setsockopt_needs_rtnl(optname);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (sockptr_is_null(optval))
40462306a36Sopenharmony_ci		val = 0;
40562306a36Sopenharmony_ci	else {
40662306a36Sopenharmony_ci		if (optlen >= sizeof(int)) {
40762306a36Sopenharmony_ci			if (copy_from_sockptr(&val, optval, sizeof(val)))
40862306a36Sopenharmony_ci				return -EFAULT;
40962306a36Sopenharmony_ci		} else
41062306a36Sopenharmony_ci			val = 0;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	valbool = (val != 0);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (ip6_mroute_opt(optname))
41662306a36Sopenharmony_ci		return ip6_mroute_setsockopt(sk, optname, optval, optlen);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (needs_rtnl)
41962306a36Sopenharmony_ci		rtnl_lock();
42062306a36Sopenharmony_ci	sockopt_lock_sock(sk);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* Another thread has converted the socket into IPv4 with
42362306a36Sopenharmony_ci	 * IPV6_ADDRFORM concurrently.
42462306a36Sopenharmony_ci	 */
42562306a36Sopenharmony_ci	if (unlikely(sk->sk_family != AF_INET6))
42662306a36Sopenharmony_ci		goto unlock;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	switch (optname) {
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	case IPV6_ADDRFORM:
43162306a36Sopenharmony_ci		if (optlen < sizeof(int))
43262306a36Sopenharmony_ci			goto e_inval;
43362306a36Sopenharmony_ci		if (val == PF_INET) {
43462306a36Sopenharmony_ci			if (sk->sk_type == SOCK_RAW)
43562306a36Sopenharmony_ci				break;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci			if (sk->sk_protocol == IPPROTO_UDP ||
43862306a36Sopenharmony_ci			    sk->sk_protocol == IPPROTO_UDPLITE) {
43962306a36Sopenharmony_ci				struct udp_sock *up = udp_sk(sk);
44062306a36Sopenharmony_ci				if (up->pending == AF_INET6) {
44162306a36Sopenharmony_ci					retv = -EBUSY;
44262306a36Sopenharmony_ci					break;
44362306a36Sopenharmony_ci				}
44462306a36Sopenharmony_ci			} else if (sk->sk_protocol == IPPROTO_TCP) {
44562306a36Sopenharmony_ci				if (sk->sk_prot != &tcpv6_prot) {
44662306a36Sopenharmony_ci					retv = -EBUSY;
44762306a36Sopenharmony_ci					break;
44862306a36Sopenharmony_ci				}
44962306a36Sopenharmony_ci			} else {
45062306a36Sopenharmony_ci				break;
45162306a36Sopenharmony_ci			}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci			if (sk->sk_state != TCP_ESTABLISHED) {
45462306a36Sopenharmony_ci				retv = -ENOTCONN;
45562306a36Sopenharmony_ci				break;
45662306a36Sopenharmony_ci			}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci			if (ipv6_only_sock(sk) ||
45962306a36Sopenharmony_ci			    !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) {
46062306a36Sopenharmony_ci				retv = -EADDRNOTAVAIL;
46162306a36Sopenharmony_ci				break;
46262306a36Sopenharmony_ci			}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci			__ipv6_sock_mc_close(sk);
46562306a36Sopenharmony_ci			__ipv6_sock_ac_close(sk);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci			if (sk->sk_protocol == IPPROTO_TCP) {
46862306a36Sopenharmony_ci				struct inet_connection_sock *icsk = inet_csk(sk);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci				sock_prot_inuse_add(net, sk->sk_prot, -1);
47162306a36Sopenharmony_ci				sock_prot_inuse_add(net, &tcp_prot, 1);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci				/* Paired with READ_ONCE(sk->sk_prot) in inet6_stream_ops */
47462306a36Sopenharmony_ci				WRITE_ONCE(sk->sk_prot, &tcp_prot);
47562306a36Sopenharmony_ci				/* Paired with READ_ONCE() in tcp_(get|set)sockopt() */
47662306a36Sopenharmony_ci				WRITE_ONCE(icsk->icsk_af_ops, &ipv4_specific);
47762306a36Sopenharmony_ci				WRITE_ONCE(sk->sk_socket->ops, &inet_stream_ops);
47862306a36Sopenharmony_ci				WRITE_ONCE(sk->sk_family, PF_INET);
47962306a36Sopenharmony_ci				tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
48062306a36Sopenharmony_ci			} else {
48162306a36Sopenharmony_ci				struct proto *prot = &udp_prot;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci				if (sk->sk_protocol == IPPROTO_UDPLITE)
48462306a36Sopenharmony_ci					prot = &udplite_prot;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci				sock_prot_inuse_add(net, sk->sk_prot, -1);
48762306a36Sopenharmony_ci				sock_prot_inuse_add(net, prot, 1);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci				/* Paired with READ_ONCE(sk->sk_prot) in inet6_dgram_ops */
49062306a36Sopenharmony_ci				WRITE_ONCE(sk->sk_prot, prot);
49162306a36Sopenharmony_ci				WRITE_ONCE(sk->sk_socket->ops, &inet_dgram_ops);
49262306a36Sopenharmony_ci				WRITE_ONCE(sk->sk_family, PF_INET);
49362306a36Sopenharmony_ci			}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci			/* Disable all options not to allocate memory anymore,
49662306a36Sopenharmony_ci			 * but there is still a race.  See the lockless path
49762306a36Sopenharmony_ci			 * in udpv6_sendmsg() and ipv6_local_rxpmtu().
49862306a36Sopenharmony_ci			 */
49962306a36Sopenharmony_ci			np->rxopt.all = 0;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci			inet6_cleanup_sock(sk);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci			module_put(THIS_MODULE);
50462306a36Sopenharmony_ci			retv = 0;
50562306a36Sopenharmony_ci			break;
50662306a36Sopenharmony_ci		}
50762306a36Sopenharmony_ci		goto e_inval;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	case IPV6_V6ONLY:
51062306a36Sopenharmony_ci		if (optlen < sizeof(int) ||
51162306a36Sopenharmony_ci		    inet_sk(sk)->inet_num)
51262306a36Sopenharmony_ci			goto e_inval;
51362306a36Sopenharmony_ci		sk->sk_ipv6only = valbool;
51462306a36Sopenharmony_ci		retv = 0;
51562306a36Sopenharmony_ci		break;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	case IPV6_RECVPKTINFO:
51862306a36Sopenharmony_ci		if (optlen < sizeof(int))
51962306a36Sopenharmony_ci			goto e_inval;
52062306a36Sopenharmony_ci		np->rxopt.bits.rxinfo = valbool;
52162306a36Sopenharmony_ci		retv = 0;
52262306a36Sopenharmony_ci		break;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	case IPV6_2292PKTINFO:
52562306a36Sopenharmony_ci		if (optlen < sizeof(int))
52662306a36Sopenharmony_ci			goto e_inval;
52762306a36Sopenharmony_ci		np->rxopt.bits.rxoinfo = valbool;
52862306a36Sopenharmony_ci		retv = 0;
52962306a36Sopenharmony_ci		break;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	case IPV6_RECVHOPLIMIT:
53262306a36Sopenharmony_ci		if (optlen < sizeof(int))
53362306a36Sopenharmony_ci			goto e_inval;
53462306a36Sopenharmony_ci		np->rxopt.bits.rxhlim = valbool;
53562306a36Sopenharmony_ci		retv = 0;
53662306a36Sopenharmony_ci		break;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	case IPV6_2292HOPLIMIT:
53962306a36Sopenharmony_ci		if (optlen < sizeof(int))
54062306a36Sopenharmony_ci			goto e_inval;
54162306a36Sopenharmony_ci		np->rxopt.bits.rxohlim = valbool;
54262306a36Sopenharmony_ci		retv = 0;
54362306a36Sopenharmony_ci		break;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	case IPV6_RECVRTHDR:
54662306a36Sopenharmony_ci		if (optlen < sizeof(int))
54762306a36Sopenharmony_ci			goto e_inval;
54862306a36Sopenharmony_ci		np->rxopt.bits.srcrt = valbool;
54962306a36Sopenharmony_ci		retv = 0;
55062306a36Sopenharmony_ci		break;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	case IPV6_2292RTHDR:
55362306a36Sopenharmony_ci		if (optlen < sizeof(int))
55462306a36Sopenharmony_ci			goto e_inval;
55562306a36Sopenharmony_ci		np->rxopt.bits.osrcrt = valbool;
55662306a36Sopenharmony_ci		retv = 0;
55762306a36Sopenharmony_ci		break;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	case IPV6_RECVHOPOPTS:
56062306a36Sopenharmony_ci		if (optlen < sizeof(int))
56162306a36Sopenharmony_ci			goto e_inval;
56262306a36Sopenharmony_ci		np->rxopt.bits.hopopts = valbool;
56362306a36Sopenharmony_ci		retv = 0;
56462306a36Sopenharmony_ci		break;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	case IPV6_2292HOPOPTS:
56762306a36Sopenharmony_ci		if (optlen < sizeof(int))
56862306a36Sopenharmony_ci			goto e_inval;
56962306a36Sopenharmony_ci		np->rxopt.bits.ohopopts = valbool;
57062306a36Sopenharmony_ci		retv = 0;
57162306a36Sopenharmony_ci		break;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	case IPV6_RECVDSTOPTS:
57462306a36Sopenharmony_ci		if (optlen < sizeof(int))
57562306a36Sopenharmony_ci			goto e_inval;
57662306a36Sopenharmony_ci		np->rxopt.bits.dstopts = valbool;
57762306a36Sopenharmony_ci		retv = 0;
57862306a36Sopenharmony_ci		break;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	case IPV6_2292DSTOPTS:
58162306a36Sopenharmony_ci		if (optlen < sizeof(int))
58262306a36Sopenharmony_ci			goto e_inval;
58362306a36Sopenharmony_ci		np->rxopt.bits.odstopts = valbool;
58462306a36Sopenharmony_ci		retv = 0;
58562306a36Sopenharmony_ci		break;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	case IPV6_TCLASS:
58862306a36Sopenharmony_ci		if (optlen < sizeof(int))
58962306a36Sopenharmony_ci			goto e_inval;
59062306a36Sopenharmony_ci		if (val < -1 || val > 0xff)
59162306a36Sopenharmony_ci			goto e_inval;
59262306a36Sopenharmony_ci		/* RFC 3542, 6.5: default traffic class of 0x0 */
59362306a36Sopenharmony_ci		if (val == -1)
59462306a36Sopenharmony_ci			val = 0;
59562306a36Sopenharmony_ci		if (sk->sk_type == SOCK_STREAM) {
59662306a36Sopenharmony_ci			val &= ~INET_ECN_MASK;
59762306a36Sopenharmony_ci			val |= np->tclass & INET_ECN_MASK;
59862306a36Sopenharmony_ci		}
59962306a36Sopenharmony_ci		if (np->tclass != val) {
60062306a36Sopenharmony_ci			np->tclass = val;
60162306a36Sopenharmony_ci			sk_dst_reset(sk);
60262306a36Sopenharmony_ci		}
60362306a36Sopenharmony_ci		retv = 0;
60462306a36Sopenharmony_ci		break;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	case IPV6_RECVTCLASS:
60762306a36Sopenharmony_ci		if (optlen < sizeof(int))
60862306a36Sopenharmony_ci			goto e_inval;
60962306a36Sopenharmony_ci		np->rxopt.bits.rxtclass = valbool;
61062306a36Sopenharmony_ci		retv = 0;
61162306a36Sopenharmony_ci		break;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	case IPV6_FLOWINFO:
61462306a36Sopenharmony_ci		if (optlen < sizeof(int))
61562306a36Sopenharmony_ci			goto e_inval;
61662306a36Sopenharmony_ci		np->rxopt.bits.rxflow = valbool;
61762306a36Sopenharmony_ci		retv = 0;
61862306a36Sopenharmony_ci		break;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	case IPV6_RECVPATHMTU:
62162306a36Sopenharmony_ci		if (optlen < sizeof(int))
62262306a36Sopenharmony_ci			goto e_inval;
62362306a36Sopenharmony_ci		np->rxopt.bits.rxpmtu = valbool;
62462306a36Sopenharmony_ci		retv = 0;
62562306a36Sopenharmony_ci		break;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	case IPV6_TRANSPARENT:
62862306a36Sopenharmony_ci		if (valbool && !sockopt_ns_capable(net->user_ns, CAP_NET_RAW) &&
62962306a36Sopenharmony_ci		    !sockopt_ns_capable(net->user_ns, CAP_NET_ADMIN)) {
63062306a36Sopenharmony_ci			retv = -EPERM;
63162306a36Sopenharmony_ci			break;
63262306a36Sopenharmony_ci		}
63362306a36Sopenharmony_ci		if (optlen < sizeof(int))
63462306a36Sopenharmony_ci			goto e_inval;
63562306a36Sopenharmony_ci		/* we don't have a separate transparent bit for IPV6 we use the one in the IPv4 socket */
63662306a36Sopenharmony_ci		inet_assign_bit(TRANSPARENT, sk, valbool);
63762306a36Sopenharmony_ci		retv = 0;
63862306a36Sopenharmony_ci		break;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	case IPV6_FREEBIND:
64162306a36Sopenharmony_ci		if (optlen < sizeof(int))
64262306a36Sopenharmony_ci			goto e_inval;
64362306a36Sopenharmony_ci		/* we also don't have a separate freebind bit for IPV6 */
64462306a36Sopenharmony_ci		inet_assign_bit(FREEBIND, sk, valbool);
64562306a36Sopenharmony_ci		retv = 0;
64662306a36Sopenharmony_ci		break;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	case IPV6_RECVORIGDSTADDR:
64962306a36Sopenharmony_ci		if (optlen < sizeof(int))
65062306a36Sopenharmony_ci			goto e_inval;
65162306a36Sopenharmony_ci		np->rxopt.bits.rxorigdstaddr = valbool;
65262306a36Sopenharmony_ci		retv = 0;
65362306a36Sopenharmony_ci		break;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	case IPV6_HOPOPTS:
65662306a36Sopenharmony_ci	case IPV6_RTHDRDSTOPTS:
65762306a36Sopenharmony_ci	case IPV6_RTHDR:
65862306a36Sopenharmony_ci	case IPV6_DSTOPTS:
65962306a36Sopenharmony_ci		retv = ipv6_set_opt_hdr(sk, optname, optval, optlen);
66062306a36Sopenharmony_ci		break;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	case IPV6_PKTINFO:
66362306a36Sopenharmony_ci	{
66462306a36Sopenharmony_ci		struct in6_pktinfo pkt;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci		if (optlen == 0)
66762306a36Sopenharmony_ci			goto e_inval;
66862306a36Sopenharmony_ci		else if (optlen < sizeof(struct in6_pktinfo) ||
66962306a36Sopenharmony_ci			 sockptr_is_null(optval))
67062306a36Sopenharmony_ci			goto e_inval;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		if (copy_from_sockptr(&pkt, optval, sizeof(pkt))) {
67362306a36Sopenharmony_ci			retv = -EFAULT;
67462306a36Sopenharmony_ci			break;
67562306a36Sopenharmony_ci		}
67662306a36Sopenharmony_ci		if (!sk_dev_equal_l3scope(sk, pkt.ipi6_ifindex))
67762306a36Sopenharmony_ci			goto e_inval;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		np->sticky_pktinfo.ipi6_ifindex = pkt.ipi6_ifindex;
68062306a36Sopenharmony_ci		np->sticky_pktinfo.ipi6_addr = pkt.ipi6_addr;
68162306a36Sopenharmony_ci		retv = 0;
68262306a36Sopenharmony_ci		break;
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	case IPV6_2292PKTOPTIONS:
68662306a36Sopenharmony_ci	{
68762306a36Sopenharmony_ci		struct ipv6_txoptions *opt = NULL;
68862306a36Sopenharmony_ci		struct msghdr msg;
68962306a36Sopenharmony_ci		struct flowi6 fl6;
69062306a36Sopenharmony_ci		struct ipcm6_cookie ipc6;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		memset(&fl6, 0, sizeof(fl6));
69362306a36Sopenharmony_ci		fl6.flowi6_oif = sk->sk_bound_dev_if;
69462306a36Sopenharmony_ci		fl6.flowi6_mark = sk->sk_mark;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci		if (optlen == 0)
69762306a36Sopenharmony_ci			goto update;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci		/* 1K is probably excessive
70062306a36Sopenharmony_ci		 * 1K is surely not enough, 2K per standard header is 16K.
70162306a36Sopenharmony_ci		 */
70262306a36Sopenharmony_ci		retv = -EINVAL;
70362306a36Sopenharmony_ci		if (optlen > 64*1024)
70462306a36Sopenharmony_ci			break;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci		opt = sock_kmalloc(sk, sizeof(*opt) + optlen, GFP_KERNEL);
70762306a36Sopenharmony_ci		retv = -ENOBUFS;
70862306a36Sopenharmony_ci		if (!opt)
70962306a36Sopenharmony_ci			break;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci		memset(opt, 0, sizeof(*opt));
71262306a36Sopenharmony_ci		refcount_set(&opt->refcnt, 1);
71362306a36Sopenharmony_ci		opt->tot_len = sizeof(*opt) + optlen;
71462306a36Sopenharmony_ci		retv = -EFAULT;
71562306a36Sopenharmony_ci		if (copy_from_sockptr(opt + 1, optval, optlen))
71662306a36Sopenharmony_ci			goto done;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		msg.msg_controllen = optlen;
71962306a36Sopenharmony_ci		msg.msg_control_is_user = false;
72062306a36Sopenharmony_ci		msg.msg_control = (void *)(opt+1);
72162306a36Sopenharmony_ci		ipc6.opt = opt;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		retv = ip6_datagram_send_ctl(net, sk, &msg, &fl6, &ipc6);
72462306a36Sopenharmony_ci		if (retv)
72562306a36Sopenharmony_ci			goto done;
72662306a36Sopenharmony_ciupdate:
72762306a36Sopenharmony_ci		retv = 0;
72862306a36Sopenharmony_ci		opt = ipv6_update_options(sk, opt);
72962306a36Sopenharmony_cidone:
73062306a36Sopenharmony_ci		if (opt) {
73162306a36Sopenharmony_ci			atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
73262306a36Sopenharmony_ci			txopt_put(opt);
73362306a36Sopenharmony_ci		}
73462306a36Sopenharmony_ci		break;
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci	case IPV6_UNICAST_HOPS:
73762306a36Sopenharmony_ci		if (optlen < sizeof(int))
73862306a36Sopenharmony_ci			goto e_inval;
73962306a36Sopenharmony_ci		if (val > 255 || val < -1)
74062306a36Sopenharmony_ci			goto e_inval;
74162306a36Sopenharmony_ci		np->hop_limit = val;
74262306a36Sopenharmony_ci		retv = 0;
74362306a36Sopenharmony_ci		break;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	case IPV6_MULTICAST_HOPS:
74662306a36Sopenharmony_ci		if (sk->sk_type == SOCK_STREAM)
74762306a36Sopenharmony_ci			break;
74862306a36Sopenharmony_ci		if (optlen < sizeof(int))
74962306a36Sopenharmony_ci			goto e_inval;
75062306a36Sopenharmony_ci		if (val > 255 || val < -1)
75162306a36Sopenharmony_ci			goto e_inval;
75262306a36Sopenharmony_ci		np->mcast_hops = (val == -1 ? IPV6_DEFAULT_MCASTHOPS : val);
75362306a36Sopenharmony_ci		retv = 0;
75462306a36Sopenharmony_ci		break;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	case IPV6_MULTICAST_LOOP:
75762306a36Sopenharmony_ci		if (optlen < sizeof(int))
75862306a36Sopenharmony_ci			goto e_inval;
75962306a36Sopenharmony_ci		if (val != valbool)
76062306a36Sopenharmony_ci			goto e_inval;
76162306a36Sopenharmony_ci		np->mc_loop = valbool;
76262306a36Sopenharmony_ci		retv = 0;
76362306a36Sopenharmony_ci		break;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	case IPV6_UNICAST_IF:
76662306a36Sopenharmony_ci	{
76762306a36Sopenharmony_ci		struct net_device *dev = NULL;
76862306a36Sopenharmony_ci		int ifindex;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci		if (optlen != sizeof(int))
77162306a36Sopenharmony_ci			goto e_inval;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci		ifindex = (__force int)ntohl((__force __be32)val);
77462306a36Sopenharmony_ci		if (ifindex == 0) {
77562306a36Sopenharmony_ci			np->ucast_oif = 0;
77662306a36Sopenharmony_ci			retv = 0;
77762306a36Sopenharmony_ci			break;
77862306a36Sopenharmony_ci		}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		dev = dev_get_by_index(net, ifindex);
78162306a36Sopenharmony_ci		retv = -EADDRNOTAVAIL;
78262306a36Sopenharmony_ci		if (!dev)
78362306a36Sopenharmony_ci			break;
78462306a36Sopenharmony_ci		dev_put(dev);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci		retv = -EINVAL;
78762306a36Sopenharmony_ci		if (sk->sk_bound_dev_if)
78862306a36Sopenharmony_ci			break;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		np->ucast_oif = ifindex;
79162306a36Sopenharmony_ci		retv = 0;
79262306a36Sopenharmony_ci		break;
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	case IPV6_MULTICAST_IF:
79662306a36Sopenharmony_ci		if (sk->sk_type == SOCK_STREAM)
79762306a36Sopenharmony_ci			break;
79862306a36Sopenharmony_ci		if (optlen < sizeof(int))
79962306a36Sopenharmony_ci			goto e_inval;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci		if (val) {
80262306a36Sopenharmony_ci			struct net_device *dev;
80362306a36Sopenharmony_ci			int midx;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci			rcu_read_lock();
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci			dev = dev_get_by_index_rcu(net, val);
80862306a36Sopenharmony_ci			if (!dev) {
80962306a36Sopenharmony_ci				rcu_read_unlock();
81062306a36Sopenharmony_ci				retv = -ENODEV;
81162306a36Sopenharmony_ci				break;
81262306a36Sopenharmony_ci			}
81362306a36Sopenharmony_ci			midx = l3mdev_master_ifindex_rcu(dev);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci			rcu_read_unlock();
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci			if (sk->sk_bound_dev_if &&
81862306a36Sopenharmony_ci			    sk->sk_bound_dev_if != val &&
81962306a36Sopenharmony_ci			    (!midx || midx != sk->sk_bound_dev_if))
82062306a36Sopenharmony_ci				goto e_inval;
82162306a36Sopenharmony_ci		}
82262306a36Sopenharmony_ci		np->mcast_oif = val;
82362306a36Sopenharmony_ci		retv = 0;
82462306a36Sopenharmony_ci		break;
82562306a36Sopenharmony_ci	case IPV6_ADD_MEMBERSHIP:
82662306a36Sopenharmony_ci	case IPV6_DROP_MEMBERSHIP:
82762306a36Sopenharmony_ci	{
82862306a36Sopenharmony_ci		struct ipv6_mreq mreq;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		if (optlen < sizeof(struct ipv6_mreq))
83162306a36Sopenharmony_ci			goto e_inval;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		retv = -EPROTO;
83462306a36Sopenharmony_ci		if (inet_test_bit(IS_ICSK, sk))
83562306a36Sopenharmony_ci			break;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci		retv = -EFAULT;
83862306a36Sopenharmony_ci		if (copy_from_sockptr(&mreq, optval, sizeof(struct ipv6_mreq)))
83962306a36Sopenharmony_ci			break;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci		if (optname == IPV6_ADD_MEMBERSHIP)
84262306a36Sopenharmony_ci			retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
84362306a36Sopenharmony_ci		else
84462306a36Sopenharmony_ci			retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
84562306a36Sopenharmony_ci		break;
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci	case IPV6_JOIN_ANYCAST:
84862306a36Sopenharmony_ci	case IPV6_LEAVE_ANYCAST:
84962306a36Sopenharmony_ci	{
85062306a36Sopenharmony_ci		struct ipv6_mreq mreq;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci		if (optlen < sizeof(struct ipv6_mreq))
85362306a36Sopenharmony_ci			goto e_inval;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci		retv = -EFAULT;
85662306a36Sopenharmony_ci		if (copy_from_sockptr(&mreq, optval, sizeof(struct ipv6_mreq)))
85762306a36Sopenharmony_ci			break;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		if (optname == IPV6_JOIN_ANYCAST)
86062306a36Sopenharmony_ci			retv = ipv6_sock_ac_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
86162306a36Sopenharmony_ci		else
86262306a36Sopenharmony_ci			retv = ipv6_sock_ac_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
86362306a36Sopenharmony_ci		break;
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci	case IPV6_MULTICAST_ALL:
86662306a36Sopenharmony_ci		if (optlen < sizeof(int))
86762306a36Sopenharmony_ci			goto e_inval;
86862306a36Sopenharmony_ci		np->mc_all = valbool;
86962306a36Sopenharmony_ci		retv = 0;
87062306a36Sopenharmony_ci		break;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	case MCAST_JOIN_GROUP:
87362306a36Sopenharmony_ci	case MCAST_LEAVE_GROUP:
87462306a36Sopenharmony_ci		if (in_compat_syscall())
87562306a36Sopenharmony_ci			retv = compat_ipv6_mcast_join_leave(sk, optname, optval,
87662306a36Sopenharmony_ci							    optlen);
87762306a36Sopenharmony_ci		else
87862306a36Sopenharmony_ci			retv = ipv6_mcast_join_leave(sk, optname, optval,
87962306a36Sopenharmony_ci						     optlen);
88062306a36Sopenharmony_ci		break;
88162306a36Sopenharmony_ci	case MCAST_JOIN_SOURCE_GROUP:
88262306a36Sopenharmony_ci	case MCAST_LEAVE_SOURCE_GROUP:
88362306a36Sopenharmony_ci	case MCAST_BLOCK_SOURCE:
88462306a36Sopenharmony_ci	case MCAST_UNBLOCK_SOURCE:
88562306a36Sopenharmony_ci		retv = do_ipv6_mcast_group_source(sk, optname, optval, optlen);
88662306a36Sopenharmony_ci		break;
88762306a36Sopenharmony_ci	case MCAST_MSFILTER:
88862306a36Sopenharmony_ci		if (in_compat_syscall())
88962306a36Sopenharmony_ci			retv = compat_ipv6_set_mcast_msfilter(sk, optval,
89062306a36Sopenharmony_ci							      optlen);
89162306a36Sopenharmony_ci		else
89262306a36Sopenharmony_ci			retv = ipv6_set_mcast_msfilter(sk, optval, optlen);
89362306a36Sopenharmony_ci		break;
89462306a36Sopenharmony_ci	case IPV6_ROUTER_ALERT:
89562306a36Sopenharmony_ci		if (optlen < sizeof(int))
89662306a36Sopenharmony_ci			goto e_inval;
89762306a36Sopenharmony_ci		retv = ip6_ra_control(sk, val);
89862306a36Sopenharmony_ci		break;
89962306a36Sopenharmony_ci	case IPV6_ROUTER_ALERT_ISOLATE:
90062306a36Sopenharmony_ci		if (optlen < sizeof(int))
90162306a36Sopenharmony_ci			goto e_inval;
90262306a36Sopenharmony_ci		np->rtalert_isolate = valbool;
90362306a36Sopenharmony_ci		retv = 0;
90462306a36Sopenharmony_ci		break;
90562306a36Sopenharmony_ci	case IPV6_MTU_DISCOVER:
90662306a36Sopenharmony_ci		if (optlen < sizeof(int))
90762306a36Sopenharmony_ci			goto e_inval;
90862306a36Sopenharmony_ci		if (val < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_OMIT)
90962306a36Sopenharmony_ci			goto e_inval;
91062306a36Sopenharmony_ci		np->pmtudisc = val;
91162306a36Sopenharmony_ci		retv = 0;
91262306a36Sopenharmony_ci		break;
91362306a36Sopenharmony_ci	case IPV6_MTU:
91462306a36Sopenharmony_ci		if (optlen < sizeof(int))
91562306a36Sopenharmony_ci			goto e_inval;
91662306a36Sopenharmony_ci		if (val && val < IPV6_MIN_MTU)
91762306a36Sopenharmony_ci			goto e_inval;
91862306a36Sopenharmony_ci		np->frag_size = val;
91962306a36Sopenharmony_ci		retv = 0;
92062306a36Sopenharmony_ci		break;
92162306a36Sopenharmony_ci	case IPV6_RECVERR:
92262306a36Sopenharmony_ci		if (optlen < sizeof(int))
92362306a36Sopenharmony_ci			goto e_inval;
92462306a36Sopenharmony_ci		np->recverr = valbool;
92562306a36Sopenharmony_ci		if (!val)
92662306a36Sopenharmony_ci			skb_errqueue_purge(&sk->sk_error_queue);
92762306a36Sopenharmony_ci		retv = 0;
92862306a36Sopenharmony_ci		break;
92962306a36Sopenharmony_ci	case IPV6_FLOWINFO_SEND:
93062306a36Sopenharmony_ci		if (optlen < sizeof(int))
93162306a36Sopenharmony_ci			goto e_inval;
93262306a36Sopenharmony_ci		np->sndflow = valbool;
93362306a36Sopenharmony_ci		retv = 0;
93462306a36Sopenharmony_ci		break;
93562306a36Sopenharmony_ci	case IPV6_FLOWLABEL_MGR:
93662306a36Sopenharmony_ci		retv = ipv6_flowlabel_opt(sk, optval, optlen);
93762306a36Sopenharmony_ci		break;
93862306a36Sopenharmony_ci	case IPV6_IPSEC_POLICY:
93962306a36Sopenharmony_ci	case IPV6_XFRM_POLICY:
94062306a36Sopenharmony_ci		retv = -EPERM;
94162306a36Sopenharmony_ci		if (!sockopt_ns_capable(net->user_ns, CAP_NET_ADMIN))
94262306a36Sopenharmony_ci			break;
94362306a36Sopenharmony_ci		retv = xfrm_user_policy(sk, optname, optval, optlen);
94462306a36Sopenharmony_ci		break;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	case IPV6_ADDR_PREFERENCES:
94762306a36Sopenharmony_ci		if (optlen < sizeof(int))
94862306a36Sopenharmony_ci			goto e_inval;
94962306a36Sopenharmony_ci		retv = __ip6_sock_set_addr_preferences(sk, val);
95062306a36Sopenharmony_ci		break;
95162306a36Sopenharmony_ci	case IPV6_MINHOPCOUNT:
95262306a36Sopenharmony_ci		if (optlen < sizeof(int))
95362306a36Sopenharmony_ci			goto e_inval;
95462306a36Sopenharmony_ci		if (val < 0 || val > 255)
95562306a36Sopenharmony_ci			goto e_inval;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci		if (val)
95862306a36Sopenharmony_ci			static_branch_enable(&ip6_min_hopcount);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci		/* tcp_v6_err() and tcp_v6_rcv() might read min_hopcount
96162306a36Sopenharmony_ci		 * while we are changing it.
96262306a36Sopenharmony_ci		 */
96362306a36Sopenharmony_ci		WRITE_ONCE(np->min_hopcount, val);
96462306a36Sopenharmony_ci		retv = 0;
96562306a36Sopenharmony_ci		break;
96662306a36Sopenharmony_ci	case IPV6_DONTFRAG:
96762306a36Sopenharmony_ci		np->dontfrag = valbool;
96862306a36Sopenharmony_ci		retv = 0;
96962306a36Sopenharmony_ci		break;
97062306a36Sopenharmony_ci	case IPV6_AUTOFLOWLABEL:
97162306a36Sopenharmony_ci		np->autoflowlabel = valbool;
97262306a36Sopenharmony_ci		np->autoflowlabel_set = 1;
97362306a36Sopenharmony_ci		retv = 0;
97462306a36Sopenharmony_ci		break;
97562306a36Sopenharmony_ci	case IPV6_RECVFRAGSIZE:
97662306a36Sopenharmony_ci		np->rxopt.bits.recvfragsize = valbool;
97762306a36Sopenharmony_ci		retv = 0;
97862306a36Sopenharmony_ci		break;
97962306a36Sopenharmony_ci	case IPV6_RECVERR_RFC4884:
98062306a36Sopenharmony_ci		if (optlen < sizeof(int))
98162306a36Sopenharmony_ci			goto e_inval;
98262306a36Sopenharmony_ci		if (val < 0 || val > 1)
98362306a36Sopenharmony_ci			goto e_inval;
98462306a36Sopenharmony_ci		np->recverr_rfc4884 = valbool;
98562306a36Sopenharmony_ci		retv = 0;
98662306a36Sopenharmony_ci		break;
98762306a36Sopenharmony_ci	}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ciunlock:
99062306a36Sopenharmony_ci	sockopt_release_sock(sk);
99162306a36Sopenharmony_ci	if (needs_rtnl)
99262306a36Sopenharmony_ci		rtnl_unlock();
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	return retv;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cie_inval:
99762306a36Sopenharmony_ci	retv = -EINVAL;
99862306a36Sopenharmony_ci	goto unlock;
99962306a36Sopenharmony_ci}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ciint ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
100262306a36Sopenharmony_ci		    unsigned int optlen)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	int err;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	if (level == SOL_IP && sk->sk_type != SOCK_RAW)
100762306a36Sopenharmony_ci		return udp_prot.setsockopt(sk, level, optname, optval, optlen);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	if (level != SOL_IPV6)
101062306a36Sopenharmony_ci		return -ENOPROTOOPT;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
101362306a36Sopenharmony_ci#ifdef CONFIG_NETFILTER
101462306a36Sopenharmony_ci	/* we need to exclude all possible ENOPROTOOPTs except default case */
101562306a36Sopenharmony_ci	if (err == -ENOPROTOOPT && optname != IPV6_IPSEC_POLICY &&
101662306a36Sopenharmony_ci			optname != IPV6_XFRM_POLICY)
101762306a36Sopenharmony_ci		err = nf_setsockopt(sk, PF_INET6, optname, optval, optlen);
101862306a36Sopenharmony_ci#endif
101962306a36Sopenharmony_ci	return err;
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_setsockopt);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_cistatic int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
102462306a36Sopenharmony_ci				  int optname, sockptr_t optval, int len)
102562306a36Sopenharmony_ci{
102662306a36Sopenharmony_ci	struct ipv6_opt_hdr *hdr;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	if (!opt)
102962306a36Sopenharmony_ci		return 0;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	switch (optname) {
103262306a36Sopenharmony_ci	case IPV6_HOPOPTS:
103362306a36Sopenharmony_ci		hdr = opt->hopopt;
103462306a36Sopenharmony_ci		break;
103562306a36Sopenharmony_ci	case IPV6_RTHDRDSTOPTS:
103662306a36Sopenharmony_ci		hdr = opt->dst0opt;
103762306a36Sopenharmony_ci		break;
103862306a36Sopenharmony_ci	case IPV6_RTHDR:
103962306a36Sopenharmony_ci		hdr = (struct ipv6_opt_hdr *)opt->srcrt;
104062306a36Sopenharmony_ci		break;
104162306a36Sopenharmony_ci	case IPV6_DSTOPTS:
104262306a36Sopenharmony_ci		hdr = opt->dst1opt;
104362306a36Sopenharmony_ci		break;
104462306a36Sopenharmony_ci	default:
104562306a36Sopenharmony_ci		return -EINVAL;	/* should not happen */
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	if (!hdr)
104962306a36Sopenharmony_ci		return 0;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	len = min_t(unsigned int, len, ipv6_optlen(hdr));
105262306a36Sopenharmony_ci	if (copy_to_sockptr(optval, hdr, len))
105362306a36Sopenharmony_ci		return -EFAULT;
105462306a36Sopenharmony_ci	return len;
105562306a36Sopenharmony_ci}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_cistatic int ipv6_get_msfilter(struct sock *sk, sockptr_t optval,
105862306a36Sopenharmony_ci			     sockptr_t optlen, int len)
105962306a36Sopenharmony_ci{
106062306a36Sopenharmony_ci	const int size0 = offsetof(struct group_filter, gf_slist_flex);
106162306a36Sopenharmony_ci	struct group_filter gsf;
106262306a36Sopenharmony_ci	int num;
106362306a36Sopenharmony_ci	int err;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	if (len < size0)
106662306a36Sopenharmony_ci		return -EINVAL;
106762306a36Sopenharmony_ci	if (copy_from_sockptr(&gsf, optval, size0))
106862306a36Sopenharmony_ci		return -EFAULT;
106962306a36Sopenharmony_ci	if (gsf.gf_group.ss_family != AF_INET6)
107062306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
107162306a36Sopenharmony_ci	num = gsf.gf_numsrc;
107262306a36Sopenharmony_ci	sockopt_lock_sock(sk);
107362306a36Sopenharmony_ci	err = ip6_mc_msfget(sk, &gsf, optval, size0);
107462306a36Sopenharmony_ci	if (!err) {
107562306a36Sopenharmony_ci		if (num > gsf.gf_numsrc)
107662306a36Sopenharmony_ci			num = gsf.gf_numsrc;
107762306a36Sopenharmony_ci		len = GROUP_FILTER_SIZE(num);
107862306a36Sopenharmony_ci		if (copy_to_sockptr(optlen, &len, sizeof(int)) ||
107962306a36Sopenharmony_ci		    copy_to_sockptr(optval, &gsf, size0))
108062306a36Sopenharmony_ci			err = -EFAULT;
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci	sockopt_release_sock(sk);
108362306a36Sopenharmony_ci	return err;
108462306a36Sopenharmony_ci}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_cistatic int compat_ipv6_get_msfilter(struct sock *sk, sockptr_t optval,
108762306a36Sopenharmony_ci				    sockptr_t optlen, int len)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	const int size0 = offsetof(struct compat_group_filter, gf_slist_flex);
109062306a36Sopenharmony_ci	struct compat_group_filter gf32;
109162306a36Sopenharmony_ci	struct group_filter gf;
109262306a36Sopenharmony_ci	int err;
109362306a36Sopenharmony_ci	int num;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	if (len < size0)
109662306a36Sopenharmony_ci		return -EINVAL;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (copy_from_sockptr(&gf32, optval, size0))
109962306a36Sopenharmony_ci		return -EFAULT;
110062306a36Sopenharmony_ci	gf.gf_interface = gf32.gf_interface;
110162306a36Sopenharmony_ci	gf.gf_fmode = gf32.gf_fmode;
110262306a36Sopenharmony_ci	num = gf.gf_numsrc = gf32.gf_numsrc;
110362306a36Sopenharmony_ci	gf.gf_group = gf32.gf_group;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	if (gf.gf_group.ss_family != AF_INET6)
110662306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	sockopt_lock_sock(sk);
110962306a36Sopenharmony_ci	err = ip6_mc_msfget(sk, &gf, optval, size0);
111062306a36Sopenharmony_ci	sockopt_release_sock(sk);
111162306a36Sopenharmony_ci	if (err)
111262306a36Sopenharmony_ci		return err;
111362306a36Sopenharmony_ci	if (num > gf.gf_numsrc)
111462306a36Sopenharmony_ci		num = gf.gf_numsrc;
111562306a36Sopenharmony_ci	len = GROUP_FILTER_SIZE(num) - (sizeof(gf)-sizeof(gf32));
111662306a36Sopenharmony_ci	if (copy_to_sockptr(optlen, &len, sizeof(int)) ||
111762306a36Sopenharmony_ci	    copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_fmode),
111862306a36Sopenharmony_ci				   &gf.gf_fmode, sizeof(gf32.gf_fmode)) ||
111962306a36Sopenharmony_ci	    copy_to_sockptr_offset(optval, offsetof(struct compat_group_filter, gf_numsrc),
112062306a36Sopenharmony_ci				   &gf.gf_numsrc, sizeof(gf32.gf_numsrc)))
112162306a36Sopenharmony_ci		return -EFAULT;
112262306a36Sopenharmony_ci	return 0;
112362306a36Sopenharmony_ci}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ciint do_ipv6_getsockopt(struct sock *sk, int level, int optname,
112662306a36Sopenharmony_ci		       sockptr_t optval, sockptr_t optlen)
112762306a36Sopenharmony_ci{
112862306a36Sopenharmony_ci	struct ipv6_pinfo *np = inet6_sk(sk);
112962306a36Sopenharmony_ci	int len;
113062306a36Sopenharmony_ci	int val;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	if (ip6_mroute_opt(optname))
113362306a36Sopenharmony_ci		return ip6_mroute_getsockopt(sk, optname, optval, optlen);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	if (copy_from_sockptr(&len, optlen, sizeof(int)))
113662306a36Sopenharmony_ci		return -EFAULT;
113762306a36Sopenharmony_ci	switch (optname) {
113862306a36Sopenharmony_ci	case IPV6_ADDRFORM:
113962306a36Sopenharmony_ci		if (sk->sk_protocol != IPPROTO_UDP &&
114062306a36Sopenharmony_ci		    sk->sk_protocol != IPPROTO_UDPLITE &&
114162306a36Sopenharmony_ci		    sk->sk_protocol != IPPROTO_TCP)
114262306a36Sopenharmony_ci			return -ENOPROTOOPT;
114362306a36Sopenharmony_ci		if (sk->sk_state != TCP_ESTABLISHED)
114462306a36Sopenharmony_ci			return -ENOTCONN;
114562306a36Sopenharmony_ci		val = sk->sk_family;
114662306a36Sopenharmony_ci		break;
114762306a36Sopenharmony_ci	case MCAST_MSFILTER:
114862306a36Sopenharmony_ci		if (in_compat_syscall())
114962306a36Sopenharmony_ci			return compat_ipv6_get_msfilter(sk, optval, optlen, len);
115062306a36Sopenharmony_ci		return ipv6_get_msfilter(sk, optval, optlen, len);
115162306a36Sopenharmony_ci	case IPV6_2292PKTOPTIONS:
115262306a36Sopenharmony_ci	{
115362306a36Sopenharmony_ci		struct msghdr msg;
115462306a36Sopenharmony_ci		struct sk_buff *skb;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci		if (sk->sk_type != SOCK_STREAM)
115762306a36Sopenharmony_ci			return -ENOPROTOOPT;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci		if (optval.is_kernel) {
116062306a36Sopenharmony_ci			msg.msg_control_is_user = false;
116162306a36Sopenharmony_ci			msg.msg_control = optval.kernel;
116262306a36Sopenharmony_ci		} else {
116362306a36Sopenharmony_ci			msg.msg_control_is_user = true;
116462306a36Sopenharmony_ci			msg.msg_control_user = optval.user;
116562306a36Sopenharmony_ci		}
116662306a36Sopenharmony_ci		msg.msg_controllen = len;
116762306a36Sopenharmony_ci		msg.msg_flags = 0;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci		sockopt_lock_sock(sk);
117062306a36Sopenharmony_ci		skb = np->pktoptions;
117162306a36Sopenharmony_ci		if (skb)
117262306a36Sopenharmony_ci			ip6_datagram_recv_ctl(sk, &msg, skb);
117362306a36Sopenharmony_ci		sockopt_release_sock(sk);
117462306a36Sopenharmony_ci		if (!skb) {
117562306a36Sopenharmony_ci			if (np->rxopt.bits.rxinfo) {
117662306a36Sopenharmony_ci				struct in6_pktinfo src_info;
117762306a36Sopenharmony_ci				src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
117862306a36Sopenharmony_ci					np->sticky_pktinfo.ipi6_ifindex;
117962306a36Sopenharmony_ci				src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr : np->sticky_pktinfo.ipi6_addr;
118062306a36Sopenharmony_ci				put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
118162306a36Sopenharmony_ci			}
118262306a36Sopenharmony_ci			if (np->rxopt.bits.rxhlim) {
118362306a36Sopenharmony_ci				int hlim = np->mcast_hops;
118462306a36Sopenharmony_ci				put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
118562306a36Sopenharmony_ci			}
118662306a36Sopenharmony_ci			if (np->rxopt.bits.rxtclass) {
118762306a36Sopenharmony_ci				int tclass = (int)ip6_tclass(np->rcv_flowinfo);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci				put_cmsg(&msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
119062306a36Sopenharmony_ci			}
119162306a36Sopenharmony_ci			if (np->rxopt.bits.rxoinfo) {
119262306a36Sopenharmony_ci				struct in6_pktinfo src_info;
119362306a36Sopenharmony_ci				src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
119462306a36Sopenharmony_ci					np->sticky_pktinfo.ipi6_ifindex;
119562306a36Sopenharmony_ci				src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr :
119662306a36Sopenharmony_ci								     np->sticky_pktinfo.ipi6_addr;
119762306a36Sopenharmony_ci				put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
119862306a36Sopenharmony_ci			}
119962306a36Sopenharmony_ci			if (np->rxopt.bits.rxohlim) {
120062306a36Sopenharmony_ci				int hlim = np->mcast_hops;
120162306a36Sopenharmony_ci				put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
120262306a36Sopenharmony_ci			}
120362306a36Sopenharmony_ci			if (np->rxopt.bits.rxflow) {
120462306a36Sopenharmony_ci				__be32 flowinfo = np->rcv_flowinfo;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci				put_cmsg(&msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
120762306a36Sopenharmony_ci			}
120862306a36Sopenharmony_ci		}
120962306a36Sopenharmony_ci		len -= msg.msg_controllen;
121062306a36Sopenharmony_ci		return copy_to_sockptr(optlen, &len, sizeof(int));
121162306a36Sopenharmony_ci	}
121262306a36Sopenharmony_ci	case IPV6_MTU:
121362306a36Sopenharmony_ci	{
121462306a36Sopenharmony_ci		struct dst_entry *dst;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci		val = 0;
121762306a36Sopenharmony_ci		rcu_read_lock();
121862306a36Sopenharmony_ci		dst = __sk_dst_get(sk);
121962306a36Sopenharmony_ci		if (dst)
122062306a36Sopenharmony_ci			val = dst_mtu(dst);
122162306a36Sopenharmony_ci		rcu_read_unlock();
122262306a36Sopenharmony_ci		if (!val)
122362306a36Sopenharmony_ci			return -ENOTCONN;
122462306a36Sopenharmony_ci		break;
122562306a36Sopenharmony_ci	}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	case IPV6_V6ONLY:
122862306a36Sopenharmony_ci		val = sk->sk_ipv6only;
122962306a36Sopenharmony_ci		break;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	case IPV6_RECVPKTINFO:
123262306a36Sopenharmony_ci		val = np->rxopt.bits.rxinfo;
123362306a36Sopenharmony_ci		break;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	case IPV6_2292PKTINFO:
123662306a36Sopenharmony_ci		val = np->rxopt.bits.rxoinfo;
123762306a36Sopenharmony_ci		break;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	case IPV6_RECVHOPLIMIT:
124062306a36Sopenharmony_ci		val = np->rxopt.bits.rxhlim;
124162306a36Sopenharmony_ci		break;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	case IPV6_2292HOPLIMIT:
124462306a36Sopenharmony_ci		val = np->rxopt.bits.rxohlim;
124562306a36Sopenharmony_ci		break;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	case IPV6_RECVRTHDR:
124862306a36Sopenharmony_ci		val = np->rxopt.bits.srcrt;
124962306a36Sopenharmony_ci		break;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	case IPV6_2292RTHDR:
125262306a36Sopenharmony_ci		val = np->rxopt.bits.osrcrt;
125362306a36Sopenharmony_ci		break;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	case IPV6_HOPOPTS:
125662306a36Sopenharmony_ci	case IPV6_RTHDRDSTOPTS:
125762306a36Sopenharmony_ci	case IPV6_RTHDR:
125862306a36Sopenharmony_ci	case IPV6_DSTOPTS:
125962306a36Sopenharmony_ci	{
126062306a36Sopenharmony_ci		struct ipv6_txoptions *opt;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci		sockopt_lock_sock(sk);
126362306a36Sopenharmony_ci		opt = rcu_dereference_protected(np->opt,
126462306a36Sopenharmony_ci						lockdep_sock_is_held(sk));
126562306a36Sopenharmony_ci		len = ipv6_getsockopt_sticky(sk, opt, optname, optval, len);
126662306a36Sopenharmony_ci		sockopt_release_sock(sk);
126762306a36Sopenharmony_ci		/* check if ipv6_getsockopt_sticky() returns err code */
126862306a36Sopenharmony_ci		if (len < 0)
126962306a36Sopenharmony_ci			return len;
127062306a36Sopenharmony_ci		return copy_to_sockptr(optlen, &len, sizeof(int));
127162306a36Sopenharmony_ci	}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	case IPV6_RECVHOPOPTS:
127462306a36Sopenharmony_ci		val = np->rxopt.bits.hopopts;
127562306a36Sopenharmony_ci		break;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	case IPV6_2292HOPOPTS:
127862306a36Sopenharmony_ci		val = np->rxopt.bits.ohopopts;
127962306a36Sopenharmony_ci		break;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	case IPV6_RECVDSTOPTS:
128262306a36Sopenharmony_ci		val = np->rxopt.bits.dstopts;
128362306a36Sopenharmony_ci		break;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	case IPV6_2292DSTOPTS:
128662306a36Sopenharmony_ci		val = np->rxopt.bits.odstopts;
128762306a36Sopenharmony_ci		break;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	case IPV6_TCLASS:
129062306a36Sopenharmony_ci		val = np->tclass;
129162306a36Sopenharmony_ci		break;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	case IPV6_RECVTCLASS:
129462306a36Sopenharmony_ci		val = np->rxopt.bits.rxtclass;
129562306a36Sopenharmony_ci		break;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	case IPV6_FLOWINFO:
129862306a36Sopenharmony_ci		val = np->rxopt.bits.rxflow;
129962306a36Sopenharmony_ci		break;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	case IPV6_RECVPATHMTU:
130262306a36Sopenharmony_ci		val = np->rxopt.bits.rxpmtu;
130362306a36Sopenharmony_ci		break;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	case IPV6_PATHMTU:
130662306a36Sopenharmony_ci	{
130762306a36Sopenharmony_ci		struct dst_entry *dst;
130862306a36Sopenharmony_ci		struct ip6_mtuinfo mtuinfo;
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci		if (len < sizeof(mtuinfo))
131162306a36Sopenharmony_ci			return -EINVAL;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci		len = sizeof(mtuinfo);
131462306a36Sopenharmony_ci		memset(&mtuinfo, 0, sizeof(mtuinfo));
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci		rcu_read_lock();
131762306a36Sopenharmony_ci		dst = __sk_dst_get(sk);
131862306a36Sopenharmony_ci		if (dst)
131962306a36Sopenharmony_ci			mtuinfo.ip6m_mtu = dst_mtu(dst);
132062306a36Sopenharmony_ci		rcu_read_unlock();
132162306a36Sopenharmony_ci		if (!mtuinfo.ip6m_mtu)
132262306a36Sopenharmony_ci			return -ENOTCONN;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci		if (copy_to_sockptr(optlen, &len, sizeof(int)))
132562306a36Sopenharmony_ci			return -EFAULT;
132662306a36Sopenharmony_ci		if (copy_to_sockptr(optval, &mtuinfo, len))
132762306a36Sopenharmony_ci			return -EFAULT;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci		return 0;
133062306a36Sopenharmony_ci	}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	case IPV6_TRANSPARENT:
133362306a36Sopenharmony_ci		val = inet_test_bit(TRANSPARENT, sk);
133462306a36Sopenharmony_ci		break;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	case IPV6_FREEBIND:
133762306a36Sopenharmony_ci		val = inet_test_bit(FREEBIND, sk);
133862306a36Sopenharmony_ci		break;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	case IPV6_RECVORIGDSTADDR:
134162306a36Sopenharmony_ci		val = np->rxopt.bits.rxorigdstaddr;
134262306a36Sopenharmony_ci		break;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	case IPV6_UNICAST_HOPS:
134562306a36Sopenharmony_ci	case IPV6_MULTICAST_HOPS:
134662306a36Sopenharmony_ci	{
134762306a36Sopenharmony_ci		struct dst_entry *dst;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci		if (optname == IPV6_UNICAST_HOPS)
135062306a36Sopenharmony_ci			val = np->hop_limit;
135162306a36Sopenharmony_ci		else
135262306a36Sopenharmony_ci			val = np->mcast_hops;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci		if (val < 0) {
135562306a36Sopenharmony_ci			rcu_read_lock();
135662306a36Sopenharmony_ci			dst = __sk_dst_get(sk);
135762306a36Sopenharmony_ci			if (dst)
135862306a36Sopenharmony_ci				val = ip6_dst_hoplimit(dst);
135962306a36Sopenharmony_ci			rcu_read_unlock();
136062306a36Sopenharmony_ci		}
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci		if (val < 0)
136362306a36Sopenharmony_ci			val = sock_net(sk)->ipv6.devconf_all->hop_limit;
136462306a36Sopenharmony_ci		break;
136562306a36Sopenharmony_ci	}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	case IPV6_MULTICAST_LOOP:
136862306a36Sopenharmony_ci		val = np->mc_loop;
136962306a36Sopenharmony_ci		break;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	case IPV6_MULTICAST_IF:
137262306a36Sopenharmony_ci		val = np->mcast_oif;
137362306a36Sopenharmony_ci		break;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	case IPV6_MULTICAST_ALL:
137662306a36Sopenharmony_ci		val = np->mc_all;
137762306a36Sopenharmony_ci		break;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	case IPV6_UNICAST_IF:
138062306a36Sopenharmony_ci		val = (__force int)htonl((__u32) np->ucast_oif);
138162306a36Sopenharmony_ci		break;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	case IPV6_MTU_DISCOVER:
138462306a36Sopenharmony_ci		val = np->pmtudisc;
138562306a36Sopenharmony_ci		break;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	case IPV6_RECVERR:
138862306a36Sopenharmony_ci		val = np->recverr;
138962306a36Sopenharmony_ci		break;
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	case IPV6_FLOWINFO_SEND:
139262306a36Sopenharmony_ci		val = np->sndflow;
139362306a36Sopenharmony_ci		break;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	case IPV6_FLOWLABEL_MGR:
139662306a36Sopenharmony_ci	{
139762306a36Sopenharmony_ci		struct in6_flowlabel_req freq;
139862306a36Sopenharmony_ci		int flags;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci		if (len < sizeof(freq))
140162306a36Sopenharmony_ci			return -EINVAL;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci		if (copy_from_sockptr(&freq, optval, sizeof(freq)))
140462306a36Sopenharmony_ci			return -EFAULT;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci		if (freq.flr_action != IPV6_FL_A_GET)
140762306a36Sopenharmony_ci			return -EINVAL;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci		len = sizeof(freq);
141062306a36Sopenharmony_ci		flags = freq.flr_flags;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci		memset(&freq, 0, sizeof(freq));
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci		val = ipv6_flowlabel_opt_get(sk, &freq, flags);
141562306a36Sopenharmony_ci		if (val < 0)
141662306a36Sopenharmony_ci			return val;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci		if (copy_to_sockptr(optlen, &len, sizeof(int)))
141962306a36Sopenharmony_ci			return -EFAULT;
142062306a36Sopenharmony_ci		if (copy_to_sockptr(optval, &freq, len))
142162306a36Sopenharmony_ci			return -EFAULT;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci		return 0;
142462306a36Sopenharmony_ci	}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	case IPV6_ADDR_PREFERENCES:
142762306a36Sopenharmony_ci		val = 0;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci		if (np->srcprefs & IPV6_PREFER_SRC_TMP)
143062306a36Sopenharmony_ci			val |= IPV6_PREFER_SRC_TMP;
143162306a36Sopenharmony_ci		else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC)
143262306a36Sopenharmony_ci			val |= IPV6_PREFER_SRC_PUBLIC;
143362306a36Sopenharmony_ci		else {
143462306a36Sopenharmony_ci			/* XXX: should we return system default? */
143562306a36Sopenharmony_ci			val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT;
143662306a36Sopenharmony_ci		}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci		if (np->srcprefs & IPV6_PREFER_SRC_COA)
143962306a36Sopenharmony_ci			val |= IPV6_PREFER_SRC_COA;
144062306a36Sopenharmony_ci		else
144162306a36Sopenharmony_ci			val |= IPV6_PREFER_SRC_HOME;
144262306a36Sopenharmony_ci		break;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	case IPV6_MINHOPCOUNT:
144562306a36Sopenharmony_ci		val = np->min_hopcount;
144662306a36Sopenharmony_ci		break;
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	case IPV6_DONTFRAG:
144962306a36Sopenharmony_ci		val = np->dontfrag;
145062306a36Sopenharmony_ci		break;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	case IPV6_AUTOFLOWLABEL:
145362306a36Sopenharmony_ci		val = ip6_autoflowlabel(sock_net(sk), np);
145462306a36Sopenharmony_ci		break;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	case IPV6_RECVFRAGSIZE:
145762306a36Sopenharmony_ci		val = np->rxopt.bits.recvfragsize;
145862306a36Sopenharmony_ci		break;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	case IPV6_ROUTER_ALERT_ISOLATE:
146162306a36Sopenharmony_ci		val = np->rtalert_isolate;
146262306a36Sopenharmony_ci		break;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	case IPV6_RECVERR_RFC4884:
146562306a36Sopenharmony_ci		val = np->recverr_rfc4884;
146662306a36Sopenharmony_ci		break;
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	default:
146962306a36Sopenharmony_ci		return -ENOPROTOOPT;
147062306a36Sopenharmony_ci	}
147162306a36Sopenharmony_ci	len = min_t(unsigned int, sizeof(int), len);
147262306a36Sopenharmony_ci	if (copy_to_sockptr(optlen, &len, sizeof(int)))
147362306a36Sopenharmony_ci		return -EFAULT;
147462306a36Sopenharmony_ci	if (copy_to_sockptr(optval, &val, len))
147562306a36Sopenharmony_ci		return -EFAULT;
147662306a36Sopenharmony_ci	return 0;
147762306a36Sopenharmony_ci}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ciint ipv6_getsockopt(struct sock *sk, int level, int optname,
148062306a36Sopenharmony_ci		    char __user *optval, int __user *optlen)
148162306a36Sopenharmony_ci{
148262306a36Sopenharmony_ci	int err;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	if (level == SOL_IP && sk->sk_type != SOCK_RAW)
148562306a36Sopenharmony_ci		return udp_prot.getsockopt(sk, level, optname, optval, optlen);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	if (level != SOL_IPV6)
148862306a36Sopenharmony_ci		return -ENOPROTOOPT;
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	err = do_ipv6_getsockopt(sk, level, optname,
149162306a36Sopenharmony_ci				 USER_SOCKPTR(optval), USER_SOCKPTR(optlen));
149262306a36Sopenharmony_ci#ifdef CONFIG_NETFILTER
149362306a36Sopenharmony_ci	/* we need to exclude all possible ENOPROTOOPTs except default case */
149462306a36Sopenharmony_ci	if (err == -ENOPROTOOPT && optname != IPV6_2292PKTOPTIONS) {
149562306a36Sopenharmony_ci		int len;
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci		if (get_user(len, optlen))
149862306a36Sopenharmony_ci			return -EFAULT;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci		err = nf_getsockopt(sk, PF_INET6, optname, optval, &len);
150162306a36Sopenharmony_ci		if (err >= 0)
150262306a36Sopenharmony_ci			err = put_user(len, optlen);
150362306a36Sopenharmony_ci	}
150462306a36Sopenharmony_ci#endif
150562306a36Sopenharmony_ci	return err;
150662306a36Sopenharmony_ci}
150762306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_getsockopt);
1508