nfsd: minor write_pool_threads exit cleanup
[safe/jmp/linux-2.6] / net / ipv6 / ipv6_sockglue.c
index 86e28a7..a7fdf9a 100644 (file)
@@ -7,8 +7,6 @@
  *
  *     Based on linux/net/ipv4/ip_sockglue.c
  *
- *     $Id: ipv6_sockglue.c,v 1.41 2002/02/01 22:01:04 davem Exp $
- *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
  *      as published by the Free Software Foundation; either version
@@ -61,7 +59,7 @@ DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly;
 struct ip6_ra_chain *ip6_ra_chain;
 DEFINE_RWLOCK(ip6_ra_lock);
 
-int ip6_ra_control(struct sock *sk, int sel, void (*destructor)(struct sock *))
+int ip6_ra_control(struct sock *sk, int sel)
 {
        struct ip6_ra_chain *ra, *new_ra, **rap;
 
@@ -83,8 +81,6 @@ int ip6_ra_control(struct sock *sk, int sel, void (*destructor)(struct sock *))
                        *rap = ra->next;
                        write_unlock_bh(&ip6_ra_lock);
 
-                       if (ra->destructor)
-                               ra->destructor(sk);
                        sock_put(sk);
                        kfree(ra);
                        return 0;
@@ -96,7 +92,6 @@ int ip6_ra_control(struct sock *sk, int sel, void (*destructor)(struct sock *))
        }
        new_ra->sk = sk;
        new_ra->sel = sel;
-       new_ra->destructor = destructor;
        new_ra->next = ra;
        *rap = new_ra;
        sock_hold(sk);
@@ -223,8 +218,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                        if (opt)
                                sock_kfree_s(sk, opt, opt->tot_len);
                        pktopt = xchg(&np->pktoptions, NULL);
-                       if (pktopt)
-                               kfree_skb(pktopt);
+                       kfree_skb(pktopt);
 
                        sk->sk_destruct = inet_sock_destruct;
                        /*
@@ -351,6 +345,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                 */
                if (optlen == 0)
                        optval = NULL;
+               else if (optval == NULL)
+                       goto e_inval;
                else if (optlen < sizeof(struct ipv6_opt_hdr) ||
                         optlen & 0x7 || optlen > 8 * 255)
                        goto e_inval;
@@ -369,20 +365,21 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                }
 
                /* routing header option needs extra check */
+               retv = -EINVAL;
                if (optname == IPV6_RTHDR && opt && opt->srcrt) {
                        struct ipv6_rt_hdr *rthdr = opt->srcrt;
                        switch (rthdr->type) {
 #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
                        case IPV6_SRCRT_TYPE_2:
+                               if (rthdr->hdrlen != 2 ||
+                                   rthdr->segments_left != 1)
+                                       goto sticky_done;
+
                                break;
 #endif
                        default:
                                goto sticky_done;
                        }
-
-                       if ((rthdr->hdrlen & 1) ||
-                           (rthdr->hdrlen >> 1) != rthdr->segments_left)
-                               goto sticky_done;
                }
 
                retv = 0;
@@ -393,6 +390,28 @@ sticky_done:
                break;
        }
 
+       case IPV6_PKTINFO:
+       {
+               struct in6_pktinfo pkt;
+
+               if (optlen == 0)
+                       goto e_inval;
+               else if (optlen < sizeof(struct in6_pktinfo) || optval == NULL)
+                       goto e_inval;
+
+               if (copy_from_user(&pkt, optval, sizeof(struct in6_pktinfo))) {
+                               retv = -EFAULT;
+                               break;
+               }
+               if (sk->sk_bound_dev_if && pkt.ipi6_ifindex != sk->sk_bound_dev_if)
+                       goto e_inval;
+
+               np->sticky_pktinfo.ipi6_ifindex = pkt.ipi6_ifindex;
+               ipv6_addr_copy(&np->sticky_pktinfo.ipi6_addr, &pkt.ipi6_addr);
+               retv = 0;
+               break;
+       }
+
        case IPV6_2292PKTOPTIONS:
        {
                struct ipv6_txoptions *opt = NULL;
@@ -634,7 +653,7 @@ done:
        case IPV6_ROUTER_ALERT:
                if (optlen < sizeof(int))
                        goto e_inval;
-               retv = ip6_ra_control(sk, val, NULL);
+               retv = ip6_ra_control(sk, val);
                break;
        case IPV6_MTU_DISCOVER:
                if (optlen < sizeof(int))
@@ -914,8 +933,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                } else {
                        if (np->rxopt.bits.rxinfo) {
                                struct in6_pktinfo src_info;
-                               src_info.ipi6_ifindex = np->mcast_oif;
-                               ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr);
+                               src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
+                                       np->sticky_pktinfo.ipi6_ifindex;
+                               np->mcast_oif? ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr) :
+                                       ipv6_addr_copy(&src_info.ipi6_addr, &(np->sticky_pktinfo.ipi6_addr));
                                put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
                        }
                        if (np->rxopt.bits.rxhlim) {
@@ -924,8 +945,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                        }
                        if (np->rxopt.bits.rxoinfo) {
                                struct in6_pktinfo src_info;
-                               src_info.ipi6_ifindex = np->mcast_oif;
-                               ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr);
+                               src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
+                                       np->sticky_pktinfo.ipi6_ifindex;
+                               np->mcast_oif? ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr) :
+                                       ipv6_addr_copy(&src_info.ipi6_addr, &(np->sticky_pktinfo.ipi6_addr));
                                put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
                        }
                        if (np->rxopt.bits.rxohlim) {
@@ -1043,7 +1066,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                        dst_release(dst);
                }
                if (val < 0)
-                       val = ipv6_devconf.hop_limit;
+                       val = sock_net(sk)->ipv6.devconf_all->hop_limit;
                break;
        }