~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to net/ipv6/ipv6_sockglue.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

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