Branch data Line data Source code
1 : : /*
2 : : * IPv6 BSD socket options interface
3 : : * Linux INET6 implementation
4 : : *
5 : : * Authors:
6 : : * Pedro Roque <roque@di.fc.ul.pt>
7 : : *
8 : : * Based on linux/net/ipv4/ip_sockglue.c
9 : : *
10 : : * This program is free software; you can redistribute it and/or
11 : : * modify it under the terms of the GNU General Public License
12 : : * as published by the Free Software Foundation; either version
13 : : * 2 of the License, or (at your option) any later version.
14 : : *
15 : : * FIXME: Make the setsockopt code POSIX compliant: That is
16 : : *
17 : : * o Truncate getsockopt returns
18 : : * o Return an optlen of the truncated length if need be
19 : : *
20 : : * Changes:
21 : : * David L Stevens <dlstevens@us.ibm.com>:
22 : : * - added multicast source filtering API for MLDv2
23 : : */
24 : :
25 : : #include <linux/module.h>
26 : : #include <linux/capability.h>
27 : : #include <linux/errno.h>
28 : : #include <linux/types.h>
29 : : #include <linux/socket.h>
30 : : #include <linux/sockios.h>
31 : : #include <linux/net.h>
32 : : #include <linux/in6.h>
33 : : #include <linux/mroute6.h>
34 : : #include <linux/netdevice.h>
35 : : #include <linux/if_arp.h>
36 : : #include <linux/init.h>
37 : : #include <linux/sysctl.h>
38 : : #include <linux/netfilter.h>
39 : : #include <linux/slab.h>
40 : :
41 : : #include <net/sock.h>
42 : : #include <net/snmp.h>
43 : : #include <net/ipv6.h>
44 : : #include <net/ndisc.h>
45 : : #include <net/protocol.h>
46 : : #include <net/transp_v6.h>
47 : : #include <net/ip6_route.h>
48 : : #include <net/addrconf.h>
49 : : #include <net/inet_common.h>
50 : : #include <net/tcp.h>
51 : : #include <net/udp.h>
52 : : #include <net/udplite.h>
53 : : #include <net/xfrm.h>
54 : : #include <net/compat.h>
55 : :
56 : : #include <asm/uaccess.h>
57 : :
58 : : struct ip6_ra_chain *ip6_ra_chain;
59 : : DEFINE_RWLOCK(ip6_ra_lock);
60 : :
61 : 0 : int ip6_ra_control(struct sock *sk, int sel)
62 : : {
63 : : struct ip6_ra_chain *ra, *new_ra, **rap;
64 : :
65 : : /* RA packet may be delivered ONLY to IPPROTO_RAW socket */
66 [ # # ][ # # ]: 0 : if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num != IPPROTO_RAW)
67 : : return -ENOPROTOOPT;
68 : :
69 [ # # ]: 0 : new_ra = (sel>=0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
70 : :
71 : 0 : write_lock_bh(&ip6_ra_lock);
72 [ # # ]: 0 : for (rap = &ip6_ra_chain; (ra=*rap) != NULL; rap = &ra->next) {
73 [ # # ]: 0 : if (ra->sk == sk) {
74 [ # # ]: 0 : if (sel>=0) {
75 : 0 : write_unlock_bh(&ip6_ra_lock);
76 : 0 : kfree(new_ra);
77 : 0 : return -EADDRINUSE;
78 : : }
79 : :
80 : 0 : *rap = ra->next;
81 : 0 : write_unlock_bh(&ip6_ra_lock);
82 : :
83 : : sock_put(sk);
84 : 0 : kfree(ra);
85 : 0 : return 0;
86 : : }
87 : : }
88 [ # # ]: 0 : if (new_ra == NULL) {
89 : 0 : write_unlock_bh(&ip6_ra_lock);
90 : 0 : return -ENOBUFS;
91 : : }
92 : 0 : new_ra->sk = sk;
93 : 0 : new_ra->sel = sel;
94 : 0 : new_ra->next = ra;
95 : 0 : *rap = new_ra;
96 : : sock_hold(sk);
97 : 0 : write_unlock_bh(&ip6_ra_lock);
98 : 0 : return 0;
99 : : }
100 : :
101 : : static
102 : 0 : struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
103 : : struct ipv6_txoptions *opt)
104 : : {
105 [ # # ]: 0 : if (inet_sk(sk)->is_icsk) {
106 [ # # ][ # # ]: 0 : if (opt &&
107 [ # # ]: 0 : !((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) &&
108 : 0 : inet_sk(sk)->inet_daddr != LOOPBACK4_IPV6) {
109 : : struct inet_connection_sock *icsk = inet_csk(sk);
110 : 0 : icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen;
111 : 0 : icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
112 : : }
113 : 0 : opt = xchg(&inet6_sk(sk)->opt, opt);
114 : : } else {
115 : : spin_lock(&sk->sk_dst_lock);
116 : 0 : opt = xchg(&inet6_sk(sk)->opt, opt);
117 : : spin_unlock(&sk->sk_dst_lock);
118 : : }
119 : : sk_dst_reset(sk);
120 : :
121 : 0 : return opt;
122 : : }
123 : :
124 : 0 : static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
125 : : char __user *optval, unsigned int optlen)
126 : : {
127 : : struct ipv6_pinfo *np = inet6_sk(sk);
128 : : struct net *net = sock_net(sk);
129 : : int val, valbool;
130 : : int retv = -ENOPROTOOPT;
131 : :
132 [ # # ]: 0 : if (optval == NULL)
133 : : val=0;
134 : : else {
135 [ # # ]: 0 : if (optlen >= sizeof(int)) {
136 [ # # ]: 0 : if (get_user(val, (int __user *) optval))
137 : : return -EFAULT;
138 : : } else
139 : : val = 0;
140 : : }
141 : :
142 : 0 : valbool = (val!=0);
143 : :
144 : : if (ip6_mroute_opt(optname))
145 : : return ip6_mroute_setsockopt(sk, optname, optval, optlen);
146 : :
147 : : lock_sock(sk);
148 : :
149 [ # # # # : 0 : switch (optname) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
150 : :
151 : : case IPV6_ADDRFORM:
152 [ # # ]: 0 : if (optlen < sizeof(int))
153 : : goto e_inval;
154 [ # # ]: 0 : if (val == PF_INET) {
155 : : struct ipv6_txoptions *opt;
156 : : struct sk_buff *pktopt;
157 : :
158 [ # # ]: 0 : if (sk->sk_type == SOCK_RAW)
159 : : break;
160 : :
161 [ # # ]: 0 : if (sk->sk_protocol == IPPROTO_UDP ||
162 : : sk->sk_protocol == IPPROTO_UDPLITE) {
163 : : struct udp_sock *up = udp_sk(sk);
164 [ # # ]: 0 : if (up->pending == AF_INET6) {
165 : : retv = -EBUSY;
166 : : break;
167 : : }
168 [ # # ]: 0 : } else if (sk->sk_protocol != IPPROTO_TCP)
169 : : break;
170 : :
171 [ # # ]: 0 : if (sk->sk_state != TCP_ESTABLISHED) {
172 : : retv = -ENOTCONN;
173 : : break;
174 : : }
175 : :
176 [ # # ][ # # ]: 0 : if (ipv6_only_sock(sk) ||
[ # # ]
177 : : !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) {
178 : : retv = -EADDRNOTAVAIL;
179 : : break;
180 : : }
181 : :
182 : 0 : fl6_free_socklist(sk);
183 : 0 : ipv6_sock_mc_close(sk);
184 : :
185 : : /*
186 : : * Sock is moving from IPv6 to IPv4 (sk_prot), so
187 : : * remove it from the refcnt debug socks count in the
188 : : * original family...
189 : : */
190 : : sk_refcnt_debug_dec(sk);
191 : :
192 [ # # ]: 0 : if (sk->sk_protocol == IPPROTO_TCP) {
193 : : struct inet_connection_sock *icsk = inet_csk(sk);
194 : : local_bh_disable();
195 : 0 : sock_prot_inuse_add(net, sk->sk_prot, -1);
196 : 0 : sock_prot_inuse_add(net, &tcp_prot, 1);
197 : : local_bh_enable();
198 : 0 : sk->sk_prot = &tcp_prot;
199 : 0 : icsk->icsk_af_ops = &ipv4_specific;
200 : 0 : sk->sk_socket->ops = &inet_stream_ops;
201 : 0 : sk->sk_family = PF_INET;
202 : 0 : tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
203 : : } else {
204 : : struct proto *prot = &udp_prot;
205 : :
206 [ # # ]: 0 : if (sk->sk_protocol == IPPROTO_UDPLITE)
207 : : prot = &udplite_prot;
208 : : local_bh_disable();
209 : 0 : sock_prot_inuse_add(net, sk->sk_prot, -1);
210 : 0 : sock_prot_inuse_add(net, prot, 1);
211 : : local_bh_enable();
212 : 0 : sk->sk_prot = prot;
213 : 0 : sk->sk_socket->ops = &inet_dgram_ops;
214 : 0 : sk->sk_family = PF_INET;
215 : : }
216 : 0 : opt = xchg(&np->opt, NULL);
217 [ # # ]: 0 : if (opt)
218 : 0 : sock_kfree_s(sk, opt, opt->tot_len);
219 : 0 : pktopt = xchg(&np->pktoptions, NULL);
220 : 0 : kfree_skb(pktopt);
221 : :
222 : 0 : sk->sk_destruct = inet_sock_destruct;
223 : : /*
224 : : * ... and add it to the refcnt debug socks count
225 : : * in the new family. -acme
226 : : */
227 : : sk_refcnt_debug_inc(sk);
228 : 0 : module_put(THIS_MODULE);
229 : : retv = 0;
230 : : break;
231 : : }
232 : : goto e_inval;
233 : :
234 : : case IPV6_V6ONLY:
235 [ # # ][ # # ]: 0 : if (optlen < sizeof(int) ||
236 : 0 : inet_sk(sk)->inet_num)
237 : : goto e_inval;
238 : 0 : np->ipv6only = valbool;
239 : : retv = 0;
240 : : break;
241 : :
242 : : case IPV6_RECVPKTINFO:
243 [ # # ]: 0 : if (optlen < sizeof(int))
244 : : goto e_inval;
245 : 0 : np->rxopt.bits.rxinfo = valbool;
246 : : retv = 0;
247 : : break;
248 : :
249 : : case IPV6_2292PKTINFO:
250 [ # # ]: 0 : if (optlen < sizeof(int))
251 : : goto e_inval;
252 : 0 : np->rxopt.bits.rxoinfo = valbool;
253 : : retv = 0;
254 : : break;
255 : :
256 : : case IPV6_RECVHOPLIMIT:
257 [ # # ]: 0 : if (optlen < sizeof(int))
258 : : goto e_inval;
259 : 0 : np->rxopt.bits.rxhlim = valbool;
260 : : retv = 0;
261 : : break;
262 : :
263 : : case IPV6_2292HOPLIMIT:
264 [ # # ]: 0 : if (optlen < sizeof(int))
265 : : goto e_inval;
266 : 0 : np->rxopt.bits.rxohlim = valbool;
267 : : retv = 0;
268 : : break;
269 : :
270 : : case IPV6_RECVRTHDR:
271 [ # # ]: 0 : if (optlen < sizeof(int))
272 : : goto e_inval;
273 : 0 : np->rxopt.bits.srcrt = valbool;
274 : : retv = 0;
275 : : break;
276 : :
277 : : case IPV6_2292RTHDR:
278 [ # # ]: 0 : if (optlen < sizeof(int))
279 : : goto e_inval;
280 : 0 : np->rxopt.bits.osrcrt = valbool;
281 : : retv = 0;
282 : : break;
283 : :
284 : : case IPV6_RECVHOPOPTS:
285 [ # # ]: 0 : if (optlen < sizeof(int))
286 : : goto e_inval;
287 : 0 : np->rxopt.bits.hopopts = valbool;
288 : : retv = 0;
289 : : break;
290 : :
291 : : case IPV6_2292HOPOPTS:
292 [ # # ]: 0 : if (optlen < sizeof(int))
293 : : goto e_inval;
294 : 0 : np->rxopt.bits.ohopopts = valbool;
295 : : retv = 0;
296 : : break;
297 : :
298 : : case IPV6_RECVDSTOPTS:
299 [ # # ]: 0 : if (optlen < sizeof(int))
300 : : goto e_inval;
301 : 0 : np->rxopt.bits.dstopts = valbool;
302 : : retv = 0;
303 : : break;
304 : :
305 : : case IPV6_2292DSTOPTS:
306 [ # # ]: 0 : if (optlen < sizeof(int))
307 : : goto e_inval;
308 : 0 : np->rxopt.bits.odstopts = valbool;
309 : : retv = 0;
310 : : break;
311 : :
312 : : case IPV6_TCLASS:
313 [ # # ]: 0 : if (optlen < sizeof(int))
314 : : goto e_inval;
315 [ # # ]: 0 : if (val < -1 || val > 0xff)
316 : : goto e_inval;
317 : : /* RFC 3542, 6.5: default traffic class of 0x0 */
318 [ # # ]: 0 : if (val == -1)
319 : : val = 0;
320 : 0 : np->tclass = val;
321 : : retv = 0;
322 : : break;
323 : :
324 : : case IPV6_RECVTCLASS:
325 [ # # ]: 0 : if (optlen < sizeof(int))
326 : : goto e_inval;
327 : 0 : np->rxopt.bits.rxtclass = valbool;
328 : : retv = 0;
329 : : break;
330 : :
331 : : case IPV6_FLOWINFO:
332 [ # # ]: 0 : if (optlen < sizeof(int))
333 : : goto e_inval;
334 : 0 : np->rxopt.bits.rxflow = valbool;
335 : : retv = 0;
336 : : break;
337 : :
338 : : case IPV6_RECVPATHMTU:
339 [ # # ]: 0 : if (optlen < sizeof(int))
340 : : goto e_inval;
341 : 0 : np->rxopt.bits.rxpmtu = valbool;
342 : : retv = 0;
343 : : break;
344 : :
345 : : case IPV6_TRANSPARENT:
346 [ # # ]: 0 : if (valbool && !ns_capable(net->user_ns, CAP_NET_ADMIN) &&
[ # # # # ]
347 : 0 : !ns_capable(net->user_ns, CAP_NET_RAW)) {
348 : : retv = -EPERM;
349 : : break;
350 : : }
351 [ # # ]: 0 : if (optlen < sizeof(int))
352 : : goto e_inval;
353 : : /* we don't have a separate transparent bit for IPV6 we use the one in the IPv4 socket */
354 : 0 : inet_sk(sk)->transparent = valbool;
355 : : retv = 0;
356 : : break;
357 : :
358 : : case IPV6_RECVORIGDSTADDR:
359 [ # # ]: 0 : if (optlen < sizeof(int))
360 : : goto e_inval;
361 : 0 : np->rxopt.bits.rxorigdstaddr = valbool;
362 : : retv = 0;
363 : : break;
364 : :
365 : : case IPV6_HOPOPTS:
366 : : case IPV6_RTHDRDSTOPTS:
367 : : case IPV6_RTHDR:
368 : : case IPV6_DSTOPTS:
369 : : {
370 : : struct ipv6_txoptions *opt;
371 : :
372 : : /* remove any sticky options header with a zero option
373 : : * length, per RFC3542.
374 : : */
375 [ # # ]: 0 : if (optlen == 0)
376 : : optval = NULL;
377 [ # # ]: 0 : else if (optval == NULL)
378 : : goto e_inval;
379 [ # # ][ # # ]: 0 : else if (optlen < sizeof(struct ipv6_opt_hdr) ||
380 [ # # ]: 0 : optlen & 0x7 || optlen > 8 * 255)
381 : : goto e_inval;
382 : :
383 : : /* hop-by-hop / destination options are privileged option */
384 : : retv = -EPERM;
385 [ # # ][ # # ]: 0 : if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW))
386 : : break;
387 : :
388 : 0 : opt = ipv6_renew_options(sk, np->opt, optname,
389 : : (struct ipv6_opt_hdr __user *)optval,
390 : : optlen);
391 [ # # ]: 0 : if (IS_ERR(opt)) {
392 : : retv = PTR_ERR(opt);
393 : : break;
394 : : }
395 : :
396 : : /* routing header option needs extra check */
397 : : retv = -EINVAL;
398 [ # # ][ # # ]: 0 : if (optname == IPV6_RTHDR && opt && opt->srcrt) {
399 : : struct ipv6_rt_hdr *rthdr = opt->srcrt;
400 : : switch (rthdr->type) {
401 : : #if IS_ENABLED(CONFIG_IPV6_MIP6)
402 : : case IPV6_SRCRT_TYPE_2:
403 : : if (rthdr->hdrlen != 2 ||
404 : : rthdr->segments_left != 1)
405 : : goto sticky_done;
406 : :
407 : : break;
408 : : #endif
409 : : default:
410 : : goto sticky_done;
411 : : }
412 : : }
413 : :
414 : : retv = 0;
415 : 0 : opt = ipv6_update_options(sk, opt);
416 : : sticky_done:
417 [ # # ]: 0 : if (opt)
418 : 0 : sock_kfree_s(sk, opt, opt->tot_len);
419 : : break;
420 : : }
421 : :
422 : : case IPV6_PKTINFO:
423 : : {
424 : : struct in6_pktinfo pkt;
425 : :
426 [ # # ]: 0 : if (optlen == 0)
427 : : goto e_inval;
428 [ # # ]: 0 : else if (optlen < sizeof(struct in6_pktinfo) || optval == NULL)
429 : : goto e_inval;
430 : :
431 [ # # ]: 0 : if (copy_from_user(&pkt, optval, sizeof(struct in6_pktinfo))) {
432 : : retv = -EFAULT;
433 : 0 : break;
434 : : }
435 [ # # ][ # # ]: 0 : if (sk->sk_bound_dev_if && pkt.ipi6_ifindex != sk->sk_bound_dev_if)
436 : : goto e_inval;
437 : :
438 : 0 : np->sticky_pktinfo.ipi6_ifindex = pkt.ipi6_ifindex;
439 : 0 : np->sticky_pktinfo.ipi6_addr = pkt.ipi6_addr;
440 : : retv = 0;
441 : : break;
442 : : }
443 : :
444 : : case IPV6_2292PKTOPTIONS:
445 : : {
446 : : struct ipv6_txoptions *opt = NULL;
447 : : struct msghdr msg;
448 : : struct flowi6 fl6;
449 : : int junk;
450 : :
451 : 0 : memset(&fl6, 0, sizeof(fl6));
452 : 0 : fl6.flowi6_oif = sk->sk_bound_dev_if;
453 : 0 : fl6.flowi6_mark = sk->sk_mark;
454 : :
455 [ # # ]: 0 : if (optlen == 0)
456 : : goto update;
457 : :
458 : : /* 1K is probably excessive
459 : : * 1K is surely not enough, 2K per standard header is 16K.
460 : : */
461 : : retv = -EINVAL;
462 [ # # ]: 0 : if (optlen > 64*1024)
463 : : break;
464 : :
465 : 0 : opt = sock_kmalloc(sk, sizeof(*opt) + optlen, GFP_KERNEL);
466 : : retv = -ENOBUFS;
467 [ # # ]: 0 : if (opt == NULL)
468 : : break;
469 : :
470 : 0 : memset(opt, 0, sizeof(*opt));
471 : 0 : opt->tot_len = sizeof(*opt) + optlen;
472 : : retv = -EFAULT;
473 [ # # ]: 0 : if (copy_from_user(opt+1, optval, optlen))
474 : : goto done;
475 : :
476 : 0 : msg.msg_controllen = optlen;
477 : 0 : msg.msg_control = (void*)(opt+1);
478 : :
479 : 0 : retv = ip6_datagram_send_ctl(net, sk, &msg, &fl6, opt, &junk,
480 : : &junk, &junk);
481 [ # # ]: 0 : if (retv)
482 : : goto done;
483 : : update:
484 : : retv = 0;
485 : 0 : opt = ipv6_update_options(sk, opt);
486 : : done:
487 [ # # ]: 0 : if (opt)
488 : 0 : sock_kfree_s(sk, opt, opt->tot_len);
489 : : break;
490 : : }
491 : : case IPV6_UNICAST_HOPS:
492 [ # # ]: 0 : if (optlen < sizeof(int))
493 : : goto e_inval;
494 [ # # ]: 0 : if (val > 255 || val < -1)
495 : : goto e_inval;
496 : 0 : np->hop_limit = val;
497 : : retv = 0;
498 : : break;
499 : :
500 : : case IPV6_MULTICAST_HOPS:
501 [ # # ]: 0 : if (sk->sk_type == SOCK_STREAM)
502 : : break;
503 [ # # ]: 0 : if (optlen < sizeof(int))
504 : : goto e_inval;
505 [ # # ]: 0 : if (val > 255 || val < -1)
506 : : goto e_inval;
507 [ # # ]: 0 : np->mcast_hops = (val == -1 ? IPV6_DEFAULT_MCASTHOPS : val);
508 : : retv = 0;
509 : : break;
510 : :
511 : : case IPV6_MULTICAST_LOOP:
512 [ # # ]: 0 : if (optlen < sizeof(int))
513 : : goto e_inval;
514 [ # # ]: 0 : if (val != valbool)
515 : : goto e_inval;
516 : 0 : np->mc_loop = valbool;
517 : : retv = 0;
518 : : break;
519 : :
520 : : case IPV6_UNICAST_IF:
521 : : {
522 : : struct net_device *dev = NULL;
523 : : int ifindex;
524 : :
525 [ # # ]: 0 : if (optlen != sizeof(int))
526 : : goto e_inval;
527 : :
528 [ # # ]: 0 : ifindex = (__force int)ntohl((__force __be32)val);
529 [ # # ]: 0 : if (ifindex == 0) {
530 : 0 : np->ucast_oif = 0;
531 : : retv = 0;
532 : : break;
533 : : }
534 : :
535 : 0 : dev = dev_get_by_index(net, ifindex);
536 : : retv = -EADDRNOTAVAIL;
537 [ # # ]: 0 : if (!dev)
538 : : break;
539 : : dev_put(dev);
540 : :
541 : : retv = -EINVAL;
542 [ # # ]: 0 : if (sk->sk_bound_dev_if)
543 : : break;
544 : :
545 : 0 : np->ucast_oif = ifindex;
546 : : retv = 0;
547 : : break;
548 : : }
549 : :
550 : : case IPV6_MULTICAST_IF:
551 [ # # ]: 0 : if (sk->sk_type == SOCK_STREAM)
552 : : break;
553 [ # # ]: 0 : if (optlen < sizeof(int))
554 : : goto e_inval;
555 : :
556 [ # # ]: 0 : if (val) {
557 : : struct net_device *dev;
558 : :
559 [ # # ][ # # ]: 0 : if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != val)
560 : : goto e_inval;
561 : :
562 : 0 : dev = dev_get_by_index(net, val);
563 [ # # ]: 0 : if (!dev) {
564 : : retv = -ENODEV;
565 : : break;
566 : : }
567 : : dev_put(dev);
568 : : }
569 : 0 : np->mcast_oif = val;
570 : : retv = 0;
571 : : break;
572 : : case IPV6_ADD_MEMBERSHIP:
573 : : case IPV6_DROP_MEMBERSHIP:
574 : : {
575 : : struct ipv6_mreq mreq;
576 : :
577 [ # # ]: 0 : if (optlen < sizeof(struct ipv6_mreq))
578 : : goto e_inval;
579 : :
580 : : retv = -EPROTO;
581 [ # # ]: 0 : if (inet_sk(sk)->is_icsk)
582 : : break;
583 : :
584 : : retv = -EFAULT;
585 [ # # ]: 0 : if (copy_from_user(&mreq, optval, sizeof(struct ipv6_mreq)))
586 : : break;
587 : :
588 [ # # ]: 0 : if (optname == IPV6_ADD_MEMBERSHIP)
589 : 0 : retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
590 : : else
591 : 0 : retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
592 : : break;
593 : : }
594 : : case IPV6_JOIN_ANYCAST:
595 : : case IPV6_LEAVE_ANYCAST:
596 : : {
597 : : struct ipv6_mreq mreq;
598 : :
599 [ # # ]: 0 : if (optlen < sizeof(struct ipv6_mreq))
600 : : goto e_inval;
601 : :
602 : : retv = -EFAULT;
603 [ # # ]: 0 : if (copy_from_user(&mreq, optval, sizeof(struct ipv6_mreq)))
604 : : break;
605 : :
606 [ # # ]: 0 : if (optname == IPV6_JOIN_ANYCAST)
607 : 0 : retv = ipv6_sock_ac_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
608 : : else
609 : 0 : retv = ipv6_sock_ac_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
610 : : break;
611 : : }
612 : : case MCAST_JOIN_GROUP:
613 : : case MCAST_LEAVE_GROUP:
614 : : {
615 : : struct group_req greq;
616 : : struct sockaddr_in6 *psin6;
617 : :
618 [ # # ]: 0 : if (optlen < sizeof(struct group_req))
619 : : goto e_inval;
620 : :
621 : : retv = -EFAULT;
622 [ # # ]: 0 : if (copy_from_user(&greq, optval, sizeof(struct group_req)))
623 : : break;
624 [ # # ]: 0 : if (greq.gr_group.ss_family != AF_INET6) {
625 : : retv = -EADDRNOTAVAIL;
626 : : break;
627 : : }
628 : : psin6 = (struct sockaddr_in6 *)&greq.gr_group;
629 [ # # ]: 0 : if (optname == MCAST_JOIN_GROUP)
630 : 0 : retv = ipv6_sock_mc_join(sk, greq.gr_interface,
631 : : &psin6->sin6_addr);
632 : : else
633 : 0 : retv = ipv6_sock_mc_drop(sk, greq.gr_interface,
634 : : &psin6->sin6_addr);
635 : : break;
636 : : }
637 : : case MCAST_JOIN_SOURCE_GROUP:
638 : : case MCAST_LEAVE_SOURCE_GROUP:
639 : : case MCAST_BLOCK_SOURCE:
640 : : case MCAST_UNBLOCK_SOURCE:
641 : : {
642 : : struct group_source_req greqs;
643 : : int omode, add;
644 : :
645 [ # # ]: 0 : if (optlen < sizeof(struct group_source_req))
646 : : goto e_inval;
647 [ # # ]: 0 : if (copy_from_user(&greqs, optval, sizeof(greqs))) {
648 : : retv = -EFAULT;
649 : 0 : break;
650 : : }
651 [ # # ][ # # ]: 0 : if (greqs.gsr_group.ss_family != AF_INET6 ||
652 : 0 : greqs.gsr_source.ss_family != AF_INET6) {
653 : : retv = -EADDRNOTAVAIL;
654 : : break;
655 : : }
656 [ # # ]: 0 : if (optname == MCAST_BLOCK_SOURCE) {
657 : : omode = MCAST_EXCLUDE;
658 : : add = 1;
659 [ # # ]: 0 : } else if (optname == MCAST_UNBLOCK_SOURCE) {
660 : : omode = MCAST_EXCLUDE;
661 : : add = 0;
662 [ # # ]: 0 : } else if (optname == MCAST_JOIN_SOURCE_GROUP) {
663 : : struct sockaddr_in6 *psin6;
664 : :
665 : : psin6 = (struct sockaddr_in6 *)&greqs.gsr_group;
666 : 0 : retv = ipv6_sock_mc_join(sk, greqs.gsr_interface,
667 : : &psin6->sin6_addr);
668 : : /* prior join w/ different source is ok */
669 [ # # ]: 0 : if (retv && retv != -EADDRINUSE)
670 : : break;
671 : : omode = MCAST_INCLUDE;
672 : : add = 1;
673 : : } else /* MCAST_LEAVE_SOURCE_GROUP */ {
674 : : omode = MCAST_INCLUDE;
675 : : add = 0;
676 : : }
677 : 0 : retv = ip6_mc_source(add, omode, sk, &greqs);
678 : : break;
679 : : }
680 : : case MCAST_MSFILTER:
681 : : {
682 : : struct group_filter *gsf;
683 : :
684 [ # # ]: 0 : if (optlen < GROUP_FILTER_SIZE(0))
685 : : goto e_inval;
686 [ # # ]: 0 : if (optlen > sysctl_optmem_max) {
687 : : retv = -ENOBUFS;
688 : : break;
689 : : }
690 : : gsf = kmalloc(optlen,GFP_KERNEL);
691 [ # # ]: 0 : if (!gsf) {
692 : : retv = -ENOBUFS;
693 : : break;
694 : : }
695 : : retv = -EFAULT;
696 [ # # ]: 0 : if (copy_from_user(gsf, optval, optlen)) {
697 : 0 : kfree(gsf);
698 : : break;
699 : : }
700 : : /* numsrc >= (4G-140)/128 overflow in 32 bits */
701 [ # # ][ # # ]: 0 : if (gsf->gf_numsrc >= 0x1ffffffU ||
702 : 0 : gsf->gf_numsrc > sysctl_mld_max_msf) {
703 : 0 : kfree(gsf);
704 : : retv = -ENOBUFS;
705 : : break;
706 : : }
707 [ # # ]: 0 : if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) {
708 : 0 : kfree(gsf);
709 : : retv = -EINVAL;
710 : : break;
711 : : }
712 : 0 : retv = ip6_mc_msfilter(sk, gsf);
713 : 0 : kfree(gsf);
714 : :
715 : : break;
716 : : }
717 : : case IPV6_ROUTER_ALERT:
718 [ # # ]: 0 : if (optlen < sizeof(int))
719 : : goto e_inval;
720 : 0 : retv = ip6_ra_control(sk, val);
721 : : break;
722 : : case IPV6_MTU_DISCOVER:
723 [ # # ]: 0 : if (optlen < sizeof(int))
724 : : goto e_inval;
725 [ # # ]: 0 : if (val < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_INTERFACE)
726 : : goto e_inval;
727 : 0 : np->pmtudisc = val;
728 : : retv = 0;
729 : : break;
730 : : case IPV6_MTU:
731 [ # # ]: 0 : if (optlen < sizeof(int))
732 : : goto e_inval;
733 [ # # ]: 0 : if (val && val < IPV6_MIN_MTU)
734 : : goto e_inval;
735 : 0 : np->frag_size = val;
736 : : retv = 0;
737 : : break;
738 : : case IPV6_RECVERR:
739 [ # # ]: 0 : if (optlen < sizeof(int))
740 : : goto e_inval;
741 : 0 : np->recverr = valbool;
742 [ # # ]: 0 : if (!val)
743 : 0 : skb_queue_purge(&sk->sk_error_queue);
744 : : retv = 0;
745 : : break;
746 : : case IPV6_FLOWINFO_SEND:
747 [ # # ]: 0 : if (optlen < sizeof(int))
748 : : goto e_inval;
749 : 0 : np->sndflow = valbool;
750 : : retv = 0;
751 : : break;
752 : : case IPV6_FLOWLABEL_MGR:
753 : 0 : retv = ipv6_flowlabel_opt(sk, optval, optlen);
754 : : break;
755 : : case IPV6_IPSEC_POLICY:
756 : : case IPV6_XFRM_POLICY:
757 : : retv = -EPERM;
758 [ # # ]: 0 : if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
759 : : break;
760 : 0 : retv = xfrm_user_policy(sk, optname, optval, optlen);
761 : : break;
762 : :
763 : : case IPV6_ADDR_PREFERENCES:
764 : : {
765 : : unsigned int pref = 0;
766 : : unsigned int prefmask = ~0;
767 : :
768 [ # # ]: 0 : if (optlen < sizeof(int))
769 : : goto e_inval;
770 : :
771 : : retv = -EINVAL;
772 : :
773 : : /* check PUBLIC/TMP/PUBTMP_DEFAULT conflicts */
774 [ # # # # : 0 : switch (val & (IPV6_PREFER_SRC_PUBLIC|
# ]
775 : : IPV6_PREFER_SRC_TMP|
776 : : IPV6_PREFER_SRC_PUBTMP_DEFAULT)) {
777 : : case IPV6_PREFER_SRC_PUBLIC:
778 : : pref |= IPV6_PREFER_SRC_PUBLIC;
779 : : break;
780 : : case IPV6_PREFER_SRC_TMP:
781 : : pref |= IPV6_PREFER_SRC_TMP;
782 : : break;
783 : : case IPV6_PREFER_SRC_PUBTMP_DEFAULT:
784 : : break;
785 : : case 0:
786 : : goto pref_skip_pubtmp;
787 : : default:
788 : : goto e_inval;
789 : : }
790 : :
791 : : prefmask &= ~(IPV6_PREFER_SRC_PUBLIC|
792 : : IPV6_PREFER_SRC_TMP);
793 : : pref_skip_pubtmp:
794 : :
795 : : /* check HOME/COA conflicts */
796 [ # # # # ]: 0 : switch (val & (IPV6_PREFER_SRC_HOME|IPV6_PREFER_SRC_COA)) {
797 : : case IPV6_PREFER_SRC_HOME:
798 : : break;
799 : : case IPV6_PREFER_SRC_COA:
800 : 0 : pref |= IPV6_PREFER_SRC_COA;
801 : : case 0:
802 : : goto pref_skip_coa;
803 : : default:
804 : : goto e_inval;
805 : : }
806 : :
807 : 0 : prefmask &= ~IPV6_PREFER_SRC_COA;
808 : : pref_skip_coa:
809 : :
810 : : /* check CGA/NONCGA conflicts */
811 [ # # ]: 0 : switch (val & (IPV6_PREFER_SRC_CGA|IPV6_PREFER_SRC_NONCGA)) {
812 : : case IPV6_PREFER_SRC_CGA:
813 : : case IPV6_PREFER_SRC_NONCGA:
814 : : case 0:
815 : : break;
816 : : default:
817 : : goto e_inval;
818 : : }
819 : :
820 : 0 : np->srcprefs = (np->srcprefs & prefmask) | pref;
821 : : retv = 0;
822 : :
823 : : break;
824 : : }
825 : : case IPV6_MINHOPCOUNT:
826 [ # # ]: 0 : if (optlen < sizeof(int))
827 : : goto e_inval;
828 [ # # ]: 0 : if (val < 0 || val > 255)
829 : : goto e_inval;
830 : 0 : np->min_hopcount = val;
831 : : retv = 0;
832 : : break;
833 : : case IPV6_DONTFRAG:
834 : 0 : np->dontfrag = valbool;
835 : : retv = 0;
836 : : break;
837 : : }
838 : :
839 : 0 : release_sock(sk);
840 : :
841 : : return retv;
842 : :
843 : : e_inval:
844 : 0 : release_sock(sk);
845 : : return -EINVAL;
846 : : }
847 : :
848 : 0 : int ipv6_setsockopt(struct sock *sk, int level, int optname,
849 : : char __user *optval, unsigned int optlen)
850 : : {
851 : : int err;
852 : :
853 [ # # ][ # # ]: 0 : if (level == SOL_IP && sk->sk_type != SOCK_RAW)
854 : 0 : return udp_prot.setsockopt(sk, level, optname, optval, optlen);
855 : :
856 [ # # ]: 0 : if (level != SOL_IPV6)
857 : : return -ENOPROTOOPT;
858 : :
859 : 0 : err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
860 : : #ifdef CONFIG_NETFILTER
861 : : /* we need to exclude all possible ENOPROTOOPTs except default case */
862 [ # # ][ # # ]: 0 : if (err == -ENOPROTOOPT && optname != IPV6_IPSEC_POLICY &&
863 : : optname != IPV6_XFRM_POLICY) {
864 : : lock_sock(sk);
865 : 0 : err = nf_setsockopt(sk, PF_INET6, optname, optval,
866 : : optlen);
867 : 0 : release_sock(sk);
868 : : }
869 : : #endif
870 : 0 : return err;
871 : : }
872 : :
873 : : EXPORT_SYMBOL(ipv6_setsockopt);
874 : :
875 : : #ifdef CONFIG_COMPAT
876 : : int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
877 : : char __user *optval, unsigned int optlen)
878 : : {
879 : : int err;
880 : :
881 : : if (level == SOL_IP && sk->sk_type != SOCK_RAW) {
882 : : if (udp_prot.compat_setsockopt != NULL)
883 : : return udp_prot.compat_setsockopt(sk, level, optname,
884 : : optval, optlen);
885 : : return udp_prot.setsockopt(sk, level, optname, optval, optlen);
886 : : }
887 : :
888 : : if (level != SOL_IPV6)
889 : : return -ENOPROTOOPT;
890 : :
891 : : if (optname >= MCAST_JOIN_GROUP && optname <= MCAST_MSFILTER)
892 : : return compat_mc_setsockopt(sk, level, optname, optval, optlen,
893 : : ipv6_setsockopt);
894 : :
895 : : err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
896 : : #ifdef CONFIG_NETFILTER
897 : : /* we need to exclude all possible ENOPROTOOPTs except default case */
898 : : if (err == -ENOPROTOOPT && optname != IPV6_IPSEC_POLICY &&
899 : : optname != IPV6_XFRM_POLICY) {
900 : : lock_sock(sk);
901 : : err = compat_nf_setsockopt(sk, PF_INET6, optname,
902 : : optval, optlen);
903 : : release_sock(sk);
904 : : }
905 : : #endif
906 : : return err;
907 : : }
908 : :
909 : : EXPORT_SYMBOL(compat_ipv6_setsockopt);
910 : : #endif
911 : :
912 : 0 : static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
913 : : int optname, char __user *optval, int len)
914 : : {
915 : : struct ipv6_opt_hdr *hdr;
916 : :
917 [ # # ]: 0 : if (!opt)
918 : : return 0;
919 : :
920 [ # # # # : 0 : switch(optname) {
# ]
921 : : case IPV6_HOPOPTS:
922 : 0 : hdr = opt->hopopt;
923 : : break;
924 : : case IPV6_RTHDRDSTOPTS:
925 : 0 : hdr = opt->dst0opt;
926 : : break;
927 : : case IPV6_RTHDR:
928 : 0 : hdr = (struct ipv6_opt_hdr *)opt->srcrt;
929 : : break;
930 : : case IPV6_DSTOPTS:
931 : 0 : hdr = opt->dst1opt;
932 : : break;
933 : : default:
934 : : return -EINVAL; /* should not happen */
935 : : }
936 : :
937 [ # # ]: 0 : if (!hdr)
938 : : return 0;
939 : :
940 : 0 : len = min_t(unsigned int, len, ipv6_optlen(hdr));
941 [ # # ]: 0 : if (copy_to_user(optval, hdr, len))
942 : : return -EFAULT;
943 : : return len;
944 : : }
945 : :
946 : 0 : static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
947 : : char __user *optval, int __user *optlen, unsigned int flags)
948 : : {
949 : : struct ipv6_pinfo *np = inet6_sk(sk);
950 : : int len;
951 : : int val;
952 : :
953 : : if (ip6_mroute_opt(optname))
954 : : return ip6_mroute_getsockopt(sk, optname, optval, optlen);
955 : :
956 [ # # ]: 0 : if (get_user(len, optlen))
957 : : return -EFAULT;
958 [ # # # # : 0 : switch (optname) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
959 : : case IPV6_ADDRFORM:
960 [ # # ]: 0 : if (sk->sk_protocol != IPPROTO_UDP &&
961 [ # # ]: 0 : sk->sk_protocol != IPPROTO_UDPLITE &&
962 : : sk->sk_protocol != IPPROTO_TCP)
963 : : return -ENOPROTOOPT;
964 [ # # ]: 0 : if (sk->sk_state != TCP_ESTABLISHED)
965 : : return -ENOTCONN;
966 : 0 : val = sk->sk_family;
967 : : break;
968 : : case MCAST_MSFILTER:
969 : : {
970 : : struct group_filter gsf;
971 : : int err;
972 : :
973 [ # # ]: 0 : if (len < GROUP_FILTER_SIZE(0))
974 : : return -EINVAL;
975 [ # # ]: 0 : if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0)))
976 : : return -EFAULT;
977 [ # # ]: 0 : if (gsf.gf_group.ss_family != AF_INET6)
978 : : return -EADDRNOTAVAIL;
979 : : lock_sock(sk);
980 : 0 : err = ip6_mc_msfget(sk, &gsf,
981 : : (struct group_filter __user *)optval, optlen);
982 : 0 : release_sock(sk);
983 : : return err;
984 : : }
985 : :
986 : : case IPV6_2292PKTOPTIONS:
987 : : {
988 : : struct msghdr msg;
989 : : struct sk_buff *skb;
990 : :
991 [ # # ]: 0 : if (sk->sk_type != SOCK_STREAM)
992 : : return -ENOPROTOOPT;
993 : :
994 : 0 : msg.msg_control = optval;
995 : 0 : msg.msg_controllen = len;
996 : 0 : msg.msg_flags = flags;
997 : :
998 : : lock_sock(sk);
999 : 0 : skb = np->pktoptions;
1000 [ # # ]: 0 : if (skb)
1001 : 0 : atomic_inc(&skb->users);
1002 : 0 : release_sock(sk);
1003 : :
1004 [ # # ]: 0 : if (skb) {
1005 : 0 : ip6_datagram_recv_ctl(sk, &msg, skb);
1006 : 0 : kfree_skb(skb);
1007 : : } else {
1008 [ # # ]: 0 : if (np->rxopt.bits.rxinfo) {
1009 : : struct in6_pktinfo src_info;
1010 [ # # ]: 0 : src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
1011 : : np->sticky_pktinfo.ipi6_ifindex;
1012 [ # # ]: 0 : src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr : np->sticky_pktinfo.ipi6_addr;
1013 : 0 : put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
1014 : : }
1015 [ # # ]: 0 : if (np->rxopt.bits.rxhlim) {
1016 : 0 : int hlim = np->mcast_hops;
1017 : 0 : put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
1018 : : }
1019 [ # # ]: 0 : if (np->rxopt.bits.rxtclass) {
1020 : 0 : int tclass = (int)ip6_tclass(np->rcv_flowinfo);
1021 : :
1022 : 0 : put_cmsg(&msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
1023 : : }
1024 [ # # ]: 0 : if (np->rxopt.bits.rxoinfo) {
1025 : : struct in6_pktinfo src_info;
1026 [ # # ]: 0 : src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
1027 : : np->sticky_pktinfo.ipi6_ifindex;
1028 [ # # ]: 0 : src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr :
1029 : : np->sticky_pktinfo.ipi6_addr;
1030 : 0 : put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
1031 : : }
1032 [ # # ]: 0 : if (np->rxopt.bits.rxohlim) {
1033 : 0 : int hlim = np->mcast_hops;
1034 : 0 : put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
1035 : : }
1036 [ # # ]: 0 : if (np->rxopt.bits.rxflow) {
1037 : 0 : __be32 flowinfo = np->rcv_flowinfo;
1038 : :
1039 : 0 : put_cmsg(&msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
1040 : : }
1041 : : }
1042 : 0 : len -= msg.msg_controllen;
1043 : 0 : return put_user(len, optlen);
1044 : : }
1045 : : case IPV6_MTU:
1046 : : {
1047 : : struct dst_entry *dst;
1048 : :
1049 : 0 : val = 0;
1050 : : rcu_read_lock();
1051 : : dst = __sk_dst_get(sk);
1052 [ # # ]: 0 : if (dst)
1053 : 0 : val = dst_mtu(dst);
1054 : : rcu_read_unlock();
1055 [ # # ]: 0 : if (!val)
1056 : : return -ENOTCONN;
1057 : : break;
1058 : : }
1059 : :
1060 : : case IPV6_V6ONLY:
1061 : 0 : val = np->ipv6only;
1062 : : break;
1063 : :
1064 : : case IPV6_RECVPKTINFO:
1065 : 0 : val = np->rxopt.bits.rxinfo;
1066 : : break;
1067 : :
1068 : : case IPV6_2292PKTINFO:
1069 : 0 : val = np->rxopt.bits.rxoinfo;
1070 : : break;
1071 : :
1072 : : case IPV6_RECVHOPLIMIT:
1073 : 0 : val = np->rxopt.bits.rxhlim;
1074 : : break;
1075 : :
1076 : : case IPV6_2292HOPLIMIT:
1077 : 0 : val = np->rxopt.bits.rxohlim;
1078 : : break;
1079 : :
1080 : : case IPV6_RECVRTHDR:
1081 : 0 : val = np->rxopt.bits.srcrt;
1082 : : break;
1083 : :
1084 : : case IPV6_2292RTHDR:
1085 : 0 : val = np->rxopt.bits.osrcrt;
1086 : : break;
1087 : :
1088 : : case IPV6_HOPOPTS:
1089 : : case IPV6_RTHDRDSTOPTS:
1090 : : case IPV6_RTHDR:
1091 : : case IPV6_DSTOPTS:
1092 : : {
1093 : :
1094 : : lock_sock(sk);
1095 : 0 : len = ipv6_getsockopt_sticky(sk, np->opt,
1096 : : optname, optval, len);
1097 : 0 : release_sock(sk);
1098 : : /* check if ipv6_getsockopt_sticky() returns err code */
1099 [ # # ]: 0 : if (len < 0)
1100 : : return len;
1101 : 0 : return put_user(len, optlen);
1102 : : }
1103 : :
1104 : : case IPV6_RECVHOPOPTS:
1105 : 0 : val = np->rxopt.bits.hopopts;
1106 : : break;
1107 : :
1108 : : case IPV6_2292HOPOPTS:
1109 : 0 : val = np->rxopt.bits.ohopopts;
1110 : : break;
1111 : :
1112 : : case IPV6_RECVDSTOPTS:
1113 : 0 : val = np->rxopt.bits.dstopts;
1114 : : break;
1115 : :
1116 : : case IPV6_2292DSTOPTS:
1117 : 0 : val = np->rxopt.bits.odstopts;
1118 : : break;
1119 : :
1120 : : case IPV6_TCLASS:
1121 : 0 : val = np->tclass;
1122 : : break;
1123 : :
1124 : : case IPV6_RECVTCLASS:
1125 : 0 : val = np->rxopt.bits.rxtclass;
1126 : : break;
1127 : :
1128 : : case IPV6_FLOWINFO:
1129 : 0 : val = np->rxopt.bits.rxflow;
1130 : : break;
1131 : :
1132 : : case IPV6_RECVPATHMTU:
1133 : 0 : val = np->rxopt.bits.rxpmtu;
1134 : : break;
1135 : :
1136 : : case IPV6_PATHMTU:
1137 : : {
1138 : : struct dst_entry *dst;
1139 : : struct ip6_mtuinfo mtuinfo;
1140 : :
1141 [ # # ]: 0 : if (len < sizeof(mtuinfo))
1142 : : return -EINVAL;
1143 : :
1144 : : len = sizeof(mtuinfo);
1145 : 0 : memset(&mtuinfo, 0, sizeof(mtuinfo));
1146 : :
1147 : : rcu_read_lock();
1148 : : dst = __sk_dst_get(sk);
1149 [ # # ]: 0 : if (dst)
1150 : 0 : mtuinfo.ip6m_mtu = dst_mtu(dst);
1151 : : rcu_read_unlock();
1152 [ # # ]: 0 : if (!mtuinfo.ip6m_mtu)
1153 : : return -ENOTCONN;
1154 : :
1155 [ # # ]: 0 : if (put_user(len, optlen))
1156 : : return -EFAULT;
1157 [ # # ]: 0 : if (copy_to_user(optval, &mtuinfo, len))
1158 : : return -EFAULT;
1159 : :
1160 : : return 0;
1161 : : break;
1162 : : }
1163 : :
1164 : : case IPV6_TRANSPARENT:
1165 : 0 : val = inet_sk(sk)->transparent;
1166 : : break;
1167 : :
1168 : : case IPV6_RECVORIGDSTADDR:
1169 : 0 : val = np->rxopt.bits.rxorigdstaddr;
1170 : : break;
1171 : :
1172 : : case IPV6_UNICAST_HOPS:
1173 : : case IPV6_MULTICAST_HOPS:
1174 : : {
1175 : : struct dst_entry *dst;
1176 : :
1177 [ # # ]: 0 : if (optname == IPV6_UNICAST_HOPS)
1178 : 0 : val = np->hop_limit;
1179 : : else
1180 : 0 : val = np->mcast_hops;
1181 : :
1182 [ # # ]: 0 : if (val < 0) {
1183 : : rcu_read_lock();
1184 : : dst = __sk_dst_get(sk);
1185 [ # # ]: 0 : if (dst)
1186 : 0 : val = ip6_dst_hoplimit(dst);
1187 : : rcu_read_unlock();
1188 : : }
1189 : :
1190 [ # # ]: 0 : if (val < 0)
1191 : 0 : val = sock_net(sk)->ipv6.devconf_all->hop_limit;
1192 : : break;
1193 : : }
1194 : :
1195 : : case IPV6_MULTICAST_LOOP:
1196 : 0 : val = np->mc_loop;
1197 : : break;
1198 : :
1199 : : case IPV6_MULTICAST_IF:
1200 : 0 : val = np->mcast_oif;
1201 : : break;
1202 : :
1203 : : case IPV6_UNICAST_IF:
1204 [ # # ]: 0 : val = (__force int)htonl((__u32) np->ucast_oif);
1205 : : break;
1206 : :
1207 : : case IPV6_MTU_DISCOVER:
1208 : 0 : val = np->pmtudisc;
1209 : : break;
1210 : :
1211 : : case IPV6_RECVERR:
1212 : 0 : val = np->recverr;
1213 : : break;
1214 : :
1215 : : case IPV6_FLOWINFO_SEND:
1216 : 0 : val = np->sndflow;
1217 : : break;
1218 : :
1219 : : case IPV6_FLOWLABEL_MGR:
1220 : : {
1221 : : struct in6_flowlabel_req freq;
1222 : : int flags;
1223 : :
1224 [ # # ]: 0 : if (len < sizeof(freq))
1225 : : return -EINVAL;
1226 : :
1227 [ # # ]: 0 : if (copy_from_user(&freq, optval, sizeof(freq)))
1228 : : return -EFAULT;
1229 : :
1230 [ # # ]: 0 : if (freq.flr_action != IPV6_FL_A_GET)
1231 : : return -EINVAL;
1232 : :
1233 : : len = sizeof(freq);
1234 : 0 : flags = freq.flr_flags;
1235 : :
1236 : 0 : memset(&freq, 0, sizeof(freq));
1237 : :
1238 : 0 : val = ipv6_flowlabel_opt_get(sk, &freq, flags);
1239 [ # # ]: 0 : if (val < 0)
1240 : : return val;
1241 : :
1242 [ # # ]: 0 : if (put_user(len, optlen))
1243 : : return -EFAULT;
1244 [ # # ]: 0 : if (copy_to_user(optval, &freq, len))
1245 : : return -EFAULT;
1246 : :
1247 : : return 0;
1248 : : }
1249 : :
1250 : : case IPV6_ADDR_PREFERENCES:
1251 : 0 : val = 0;
1252 : :
1253 [ # # ]: 0 : if (np->srcprefs & IPV6_PREFER_SRC_TMP)
1254 : 0 : val |= IPV6_PREFER_SRC_TMP;
1255 [ # # ]: 0 : else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC)
1256 : 0 : val |= IPV6_PREFER_SRC_PUBLIC;
1257 : : else {
1258 : : /* XXX: should we return system default? */
1259 : 0 : val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT;
1260 : : }
1261 : :
1262 [ # # ]: 0 : if (np->srcprefs & IPV6_PREFER_SRC_COA)
1263 : 0 : val |= IPV6_PREFER_SRC_COA;
1264 : : else
1265 : 0 : val |= IPV6_PREFER_SRC_HOME;
1266 : : break;
1267 : :
1268 : : case IPV6_MINHOPCOUNT:
1269 : 0 : val = np->min_hopcount;
1270 : : break;
1271 : :
1272 : : case IPV6_DONTFRAG:
1273 : 0 : val = np->dontfrag;
1274 : : break;
1275 : :
1276 : : default:
1277 : : return -ENOPROTOOPT;
1278 : : }
1279 : 0 : len = min_t(unsigned int, sizeof(int), len);
1280 [ # # ]: 0 : if(put_user(len, optlen))
1281 : : return -EFAULT;
1282 [ # # ]: 0 : if(copy_to_user(optval,&val,len))
1283 : : return -EFAULT;
1284 : : return 0;
1285 : : }
1286 : :
1287 : 0 : int ipv6_getsockopt(struct sock *sk, int level, int optname,
1288 : : char __user *optval, int __user *optlen)
1289 : : {
1290 : : int err;
1291 : :
1292 [ # # ][ # # ]: 0 : if (level == SOL_IP && sk->sk_type != SOCK_RAW)
1293 : 0 : return udp_prot.getsockopt(sk, level, optname, optval, optlen);
1294 : :
1295 [ # # ]: 0 : if(level != SOL_IPV6)
1296 : : return -ENOPROTOOPT;
1297 : :
1298 : 0 : err = do_ipv6_getsockopt(sk, level, optname, optval, optlen, 0);
1299 : : #ifdef CONFIG_NETFILTER
1300 : : /* we need to exclude all possible ENOPROTOOPTs except default case */
1301 [ # # ]: 0 : if (err == -ENOPROTOOPT && optname != IPV6_2292PKTOPTIONS) {
1302 : : int len;
1303 : :
1304 [ # # ]: 0 : if (get_user(len, optlen))
1305 : 0 : return -EFAULT;
1306 : :
1307 : : lock_sock(sk);
1308 : 0 : err = nf_getsockopt(sk, PF_INET6, optname, optval,
1309 : : &len);
1310 : 0 : release_sock(sk);
1311 [ # # ]: 0 : if (err >= 0)
1312 : 0 : err = put_user(len, optlen);
1313 : : }
1314 : : #endif
1315 : 0 : return err;
1316 : : }
1317 : :
1318 : : EXPORT_SYMBOL(ipv6_getsockopt);
1319 : :
1320 : : #ifdef CONFIG_COMPAT
1321 : : int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
1322 : : char __user *optval, int __user *optlen)
1323 : : {
1324 : : int err;
1325 : :
1326 : : if (level == SOL_IP && sk->sk_type != SOCK_RAW) {
1327 : : if (udp_prot.compat_getsockopt != NULL)
1328 : : return udp_prot.compat_getsockopt(sk, level, optname,
1329 : : optval, optlen);
1330 : : return udp_prot.getsockopt(sk, level, optname, optval, optlen);
1331 : : }
1332 : :
1333 : : if (level != SOL_IPV6)
1334 : : return -ENOPROTOOPT;
1335 : :
1336 : : if (optname == MCAST_MSFILTER)
1337 : : return compat_mc_getsockopt(sk, level, optname, optval, optlen,
1338 : : ipv6_getsockopt);
1339 : :
1340 : : err = do_ipv6_getsockopt(sk, level, optname, optval, optlen,
1341 : : MSG_CMSG_COMPAT);
1342 : : #ifdef CONFIG_NETFILTER
1343 : : /* we need to exclude all possible ENOPROTOOPTs except default case */
1344 : : if (err == -ENOPROTOOPT && optname != IPV6_2292PKTOPTIONS) {
1345 : : int len;
1346 : :
1347 : : if (get_user(len, optlen))
1348 : : return -EFAULT;
1349 : :
1350 : : lock_sock(sk);
1351 : : err = compat_nf_getsockopt(sk, PF_INET6,
1352 : : optname, optval, &len);
1353 : : release_sock(sk);
1354 : : if (err >= 0)
1355 : : err = put_user(len, optlen);
1356 : : }
1357 : : #endif
1358 : : return err;
1359 : : }
1360 : :
1361 : : EXPORT_SYMBOL(compat_ipv6_getsockopt);
1362 : : #endif
1363 : :
|