Staging: sep: return -EFAULT on copy_to_user errors
[safe/jmp/linux-2.6] / net / ipv6 / ipv6_sockglue.c
index a7fdf9a..bd43f01 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/init.h>
 #include <linux/sysctl.h>
 #include <linux/netfilter.h>
+#include <linux/slab.h>
 
 #include <net/sock.h>
 #include <net/snmp.h>
@@ -64,7 +65,7 @@ int ip6_ra_control(struct sock *sk, int sel)
        struct ip6_ra_chain *ra, *new_ra, **rap;
 
        /* RA packet may be delivered ONLY to IPPROTO_RAW socket */
-       if (sk->sk_type != SOCK_RAW || inet_sk(sk)->num != IPPROTO_RAW)
+       if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num != IPPROTO_RAW)
                return -ENOPROTOOPT;
 
        new_ra = (sel>=0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
@@ -106,16 +107,16 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
        if (inet_sk(sk)->is_icsk) {
                if (opt &&
                    !((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) &&
-                   inet_sk(sk)->daddr != LOOPBACK4_IPV6) {
+                   inet_sk(sk)->inet_daddr != LOOPBACK4_IPV6) {
                        struct inet_connection_sock *icsk = inet_csk(sk);
                        icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen;
                        icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
                }
                opt = xchg(&inet6_sk(sk)->opt, opt);
        } else {
-               write_lock(&sk->sk_dst_lock);
+               spin_lock(&sk->sk_dst_lock);
                opt = xchg(&inet6_sk(sk)->opt, opt);
-               write_unlock(&sk->sk_dst_lock);
+               spin_unlock(&sk->sk_dst_lock);
        }
        sk_dst_reset(sk);
 
@@ -123,7 +124,7 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
 }
 
 static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
-                   char __user *optval, int optlen)
+                   char __user *optval, unsigned int optlen)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct net *net = sock_net(sk);
@@ -234,7 +235,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
 
        case IPV6_V6ONLY:
                if (optlen < sizeof(int) ||
-                   inet_sk(sk)->num)
+                   inet_sk(sk)->inet_num)
                        goto e_inval;
                np->ipv6only = valbool;
                retv = 0;
@@ -315,6 +316,9 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                        goto e_inval;
                if (val < -1 || val > 0xff)
                        goto e_inval;
+               /* RFC 3542, 6.5: default traffic class of 0x0 */
+               if (val == -1)
+                       val = 0;
                np->tclass = val;
                retv = 0;
                break;
@@ -333,6 +337,13 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                retv = 0;
                break;
 
+       case IPV6_RECVPATHMTU:
+               if (optlen < sizeof(int))
+                       goto e_inval;
+               np->rxopt.bits.rxpmtu = valbool;
+               retv = 0;
+               break;
+
        case IPV6_HOPOPTS:
        case IPV6_RTHDRDSTOPTS:
        case IPV6_RTHDR:
@@ -421,6 +432,7 @@ sticky_done:
 
                fl.fl6_flowlabel = 0;
                fl.oif = sk->sk_bound_dev_if;
+               fl.mark = sk->sk_mark;
 
                if (optlen == 0)
                        goto update;
@@ -446,7 +458,8 @@ sticky_done:
                msg.msg_controllen = optlen;
                msg.msg_control = (void*)(opt+1);
 
-               retv = datagram_send_ctl(net, &msg, &fl, opt, &junk, &junk);
+               retv = datagram_send_ctl(net, &msg, &fl, opt, &junk, &junk,
+                                        &junk);
                if (retv)
                        goto done;
 update:
@@ -493,13 +506,17 @@ done:
                        goto e_inval;
 
                if (val) {
+                       struct net_device *dev;
+
                        if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != val)
                                goto e_inval;
 
-                       if (__dev_get_by_index(net, val) == NULL) {
+                       dev = dev_get_by_index(net, val);
+                       if (!dev) {
                                retv = -ENODEV;
                                break;
                        }
+                       dev_put(dev);
                }
                np->mcast_oif = val;
                retv = 0;
@@ -658,7 +675,7 @@ done:
        case IPV6_MTU_DISCOVER:
                if (optlen < sizeof(int))
                        goto e_inval;
-               if (val<0 || val>3)
+               if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_PROBE)
                        goto e_inval;
                np->pmtudisc = val;
                retv = 0;
@@ -758,6 +775,17 @@ pref_skip_coa:
 
                break;
            }
+       case IPV6_MINHOPCOUNT:
+               if (optlen < sizeof(int))
+                       goto e_inval;
+               if (val < 0 || val > 255)
+                       goto e_inval;
+               np->min_hopcount = val;
+               break;
+       case IPV6_DONTFRAG:
+               np->dontfrag = valbool;
+               retv = 0;
+               break;
        }
 
        release_sock(sk);
@@ -770,7 +798,7 @@ e_inval:
 }
 
 int ipv6_setsockopt(struct sock *sk, int level, int optname,
-                   char __user *optval, int optlen)
+                   char __user *optval, unsigned int optlen)
 {
        int err;
 
@@ -798,7 +826,7 @@ EXPORT_SYMBOL(ipv6_setsockopt);
 
 #ifdef CONFIG_COMPAT
 int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
-                          char __user *optval, int optlen)
+                          char __user *optval, unsigned int optlen)
 {
        int err;
 
@@ -962,14 +990,13 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
        case IPV6_MTU:
        {
                struct dst_entry *dst;
+
                val = 0;
-               lock_sock(sk);
-               dst = sk_dst_get(sk);
-               if (dst) {
+               rcu_read_lock();
+               dst = __sk_dst_get(sk);
+               if (dst)
                        val = dst_mtu(dst);
-                       dst_release(dst);
-               }
-               release_sock(sk);
+               rcu_read_unlock();
                if (!val)
                        return -ENOTCONN;
                break;
@@ -1037,8 +1064,6 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
 
        case IPV6_TCLASS:
                val = np->tclass;
-               if (val < 0)
-                       val = 0;
                break;
 
        case IPV6_RECVTCLASS:
@@ -1049,6 +1074,38 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                val = np->rxopt.bits.rxflow;
                break;
 
+       case IPV6_RECVPATHMTU:
+               val = np->rxopt.bits.rxpmtu;
+               break;
+
+       case IPV6_PATHMTU:
+       {
+               struct dst_entry *dst;
+               struct ip6_mtuinfo mtuinfo;
+
+               if (len < sizeof(mtuinfo))
+                       return -EINVAL;
+
+               len = sizeof(mtuinfo);
+               memset(&mtuinfo, 0, sizeof(mtuinfo));
+
+               rcu_read_lock();
+               dst = __sk_dst_get(sk);
+               if (dst)
+                       mtuinfo.ip6m_mtu = dst_mtu(dst);
+               rcu_read_unlock();
+               if (!mtuinfo.ip6m_mtu)
+                       return -ENOTCONN;
+
+               if (put_user(len, optlen))
+                       return -EFAULT;
+               if (copy_to_user(optval, &mtuinfo, len))
+                       return -EFAULT;
+
+               return 0;
+               break;
+       }
+
        case IPV6_UNICAST_HOPS:
        case IPV6_MULTICAST_HOPS:
        {
@@ -1059,12 +1116,14 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                else
                        val = np->mcast_hops;
 
-               dst = sk_dst_get(sk);
-               if (dst) {
-                       if (val < 0)
+               if (val < 0) {
+                       rcu_read_lock();
+                       dst = __sk_dst_get(sk);
+                       if (dst)
                                val = ip6_dst_hoplimit(dst);
-                       dst_release(dst);
+                       rcu_read_unlock();
                }
+
                if (val < 0)
                        val = sock_net(sk)->ipv6.devconf_all->hop_limit;
                break;
@@ -1108,6 +1167,14 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                        val |= IPV6_PREFER_SRC_HOME;
                break;
 
+       case IPV6_MINHOPCOUNT:
+               val = np->min_hopcount;
+               break;
+
+       case IPV6_DONTFRAG:
+               val = np->dontfrag;
+               break;
+
        default:
                return -ENOPROTOOPT;
        }