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