]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/ipv6/ipv6_sockglue.c
Merge branch 'net-2.6.26-misc-20080412b' of git://git.linux-ipv6.org/gitroot/yoshfuji...
[mv-sheeva.git] / net / ipv6 / ipv6_sockglue.c
index c11c76cab371607194acbaaaff43f1f0f3370b7b..2f1244dc5ebfb8129856b68eef3dbec19e952863 100644 (file)
@@ -16,7 +16,6 @@
  *
  *     FIXME: Make the setsockopt code POSIX compliant: That is
  *
- *     o       Return -EINVAL for setsockopt of short lengths
  *     o       Truncate getsockopt returns
  *     o       Return an optlen of the truncated length if need be
  *
@@ -33,6 +32,7 @@
 #include <linux/sockios.h>
 #include <linux/net.h>
 #include <linux/in6.h>
+#include <linux/mroute6.h>
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
 #include <linux/init.h>
@@ -107,22 +107,32 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                    char __user *optval, int optlen)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
-       struct net *net = sk->sk_net;
+       struct net *net = sock_net(sk);
        int val, valbool;
        int retv = -ENOPROTOOPT;
 
        if (optval == NULL)
                val=0;
-       else if (get_user(val, (int __user *) optval))
-               return -EFAULT;
+       else {
+               if (optlen >= sizeof(int)) {
+                       if (get_user(val, (int __user *) optval))
+                               return -EFAULT;
+               } else
+                       val = 0;
+       }
 
        valbool = (val!=0);
 
+       if (ip6_mroute_opt(optname))
+               return ip6_mroute_setsockopt(sk, optname, optval, optlen);
+
        lock_sock(sk);
 
        switch (optname) {
 
        case IPV6_ADDRFORM:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                if (val == PF_INET) {
                        struct ipv6_txoptions *opt;
                        struct sk_buff *pktopt;
@@ -155,10 +165,9 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
 
                        if (sk->sk_protocol == IPPROTO_TCP) {
                                struct inet_connection_sock *icsk = inet_csk(sk);
-
                                local_bh_disable();
-                               sock_prot_inuse_add(sk->sk_prot, -1);
-                               sock_prot_inuse_add(&tcp_prot, 1);
+                               sock_prot_inuse_add(net, sk->sk_prot, -1);
+                               sock_prot_inuse_add(net, &tcp_prot, 1);
                                local_bh_enable();
                                sk->sk_prot = &tcp_prot;
                                icsk->icsk_af_ops = &ipv4_specific;
@@ -171,8 +180,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                                if (sk->sk_protocol == IPPROTO_UDPLITE)
                                        prot = &udplite_prot;
                                local_bh_disable();
-                               sock_prot_inuse_add(sk->sk_prot, -1);
-                               sock_prot_inuse_add(prot, 1);
+                               sock_prot_inuse_add(net, sk->sk_prot, -1);
+                               sock_prot_inuse_add(net, prot, 1);
                                local_bh_enable();
                                sk->sk_prot = prot;
                                sk->sk_socket->ops = &inet_dgram_ops;
@@ -198,63 +207,86 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                goto e_inval;
 
        case IPV6_V6ONLY:
-               if (inet_sk(sk)->num)
+               if (optlen < sizeof(int) ||
+                   inet_sk(sk)->num)
                        goto e_inval;
                np->ipv6only = valbool;
                retv = 0;
                break;
 
        case IPV6_RECVPKTINFO:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->rxopt.bits.rxinfo = valbool;
                retv = 0;
                break;
 
        case IPV6_2292PKTINFO:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->rxopt.bits.rxoinfo = valbool;
                retv = 0;
                break;
 
        case IPV6_RECVHOPLIMIT:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->rxopt.bits.rxhlim = valbool;
                retv = 0;
                break;
 
        case IPV6_2292HOPLIMIT:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->rxopt.bits.rxohlim = valbool;
                retv = 0;
                break;
 
        case IPV6_RECVRTHDR:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->rxopt.bits.srcrt = valbool;
                retv = 0;
                break;
 
        case IPV6_2292RTHDR:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->rxopt.bits.osrcrt = valbool;
                retv = 0;
                break;
 
        case IPV6_RECVHOPOPTS:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->rxopt.bits.hopopts = valbool;
                retv = 0;
                break;
 
        case IPV6_2292HOPOPTS:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->rxopt.bits.ohopopts = valbool;
                retv = 0;
                break;
 
        case IPV6_RECVDSTOPTS:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->rxopt.bits.dstopts = valbool;
                retv = 0;
                break;
 
        case IPV6_2292DSTOPTS:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->rxopt.bits.odstopts = valbool;
                retv = 0;
                break;
 
        case IPV6_TCLASS:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                if (val < -1 || val > 0xff)
                        goto e_inval;
                np->tclass = val;
@@ -262,11 +294,15 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                break;
 
        case IPV6_RECVTCLASS:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->rxopt.bits.rxtclass = valbool;
                retv = 0;
                break;
 
        case IPV6_FLOWINFO:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->rxopt.bits.rxflow = valbool;
                retv = 0;
                break;
@@ -285,9 +321,9 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                if (optname != IPV6_RTHDR && !capable(CAP_NET_RAW))
                        break;
 
-               retv = -EINVAL;
-               if (optlen & 0x7 || optlen > 8 * 255)
-                       break;
+               if (optlen < sizeof(struct ipv6_opt_hdr) ||
+                   optlen & 0x7 || optlen > 8 * 255)
+                       goto e_inval;
 
                opt = ipv6_renew_options(sk, np->opt, optname,
                                         (struct ipv6_opt_hdr __user *)optval,
@@ -405,6 +441,8 @@ done:
                break;
        }
        case IPV6_UNICAST_HOPS:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                if (val > 255 || val < -1)
                        goto e_inval;
                np->hop_limit = val;
@@ -414,6 +452,8 @@ done:
        case IPV6_MULTICAST_HOPS:
                if (sk->sk_type == SOCK_STREAM)
                        goto e_inval;
+               if (optlen < sizeof(int))
+                       goto e_inval;
                if (val > 255 || val < -1)
                        goto e_inval;
                np->mcast_hops = val;
@@ -421,6 +461,8 @@ done:
                break;
 
        case IPV6_MULTICAST_LOOP:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->mc_loop = valbool;
                retv = 0;
                break;
@@ -428,6 +470,8 @@ done:
        case IPV6_MULTICAST_IF:
                if (sk->sk_type == SOCK_STREAM)
                        goto e_inval;
+               if (optlen < sizeof(int))
+                       goto e_inval;
 
                if (val) {
                        if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != val)
@@ -446,6 +490,9 @@ done:
        {
                struct ipv6_mreq mreq;
 
+               if (optlen < sizeof(struct ipv6_mreq))
+                       goto e_inval;
+
                retv = -EPROTO;
                if (inet_sk(sk)->is_icsk)
                        break;
@@ -465,7 +512,7 @@ done:
        {
                struct ipv6_mreq mreq;
 
-               if (optlen != sizeof(struct ipv6_mreq))
+               if (optlen < sizeof(struct ipv6_mreq))
                        goto e_inval;
 
                retv = -EFAULT;
@@ -484,6 +531,9 @@ done:
                struct group_req greq;
                struct sockaddr_in6 *psin6;
 
+               if (optlen < sizeof(struct group_req))
+                       goto e_inval;
+
                retv = -EFAULT;
                if (copy_from_user(&greq, optval, sizeof(struct group_req)))
                        break;
@@ -508,7 +558,7 @@ done:
                struct group_source_req greqs;
                int omode, add;
 
-               if (optlen != sizeof(struct group_source_req))
+               if (optlen < sizeof(struct group_source_req))
                        goto e_inval;
                if (copy_from_user(&greqs, optval, sizeof(greqs))) {
                        retv = -EFAULT;
@@ -582,27 +632,37 @@ done:
                break;
        }
        case IPV6_ROUTER_ALERT:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                retv = ip6_ra_control(sk, val, NULL);
                break;
        case IPV6_MTU_DISCOVER:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                if (val<0 || val>3)
                        goto e_inval;
                np->pmtudisc = val;
                retv = 0;
                break;
        case IPV6_MTU:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                if (val && val < IPV6_MIN_MTU)
                        goto e_inval;
                np->frag_size = val;
                retv = 0;
                break;
        case IPV6_RECVERR:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->recverr = valbool;
                if (!val)
                        skb_queue_purge(&sk->sk_error_queue);
                retv = 0;
                break;
        case IPV6_FLOWINFO_SEND:
+               if (optlen < sizeof(int))
+                       goto e_inval;
                np->sndflow = valbool;
                retv = 0;
                break;
@@ -617,7 +677,70 @@ done:
                retv = xfrm_user_policy(sk, optname, optval, optlen);
                break;
 
+       case IPV6_ADDR_PREFERENCES:
+           {
+               unsigned int pref = 0;
+               unsigned int prefmask = ~0;
+
+               if (optlen < sizeof(int))
+                       goto e_inval;
+
+               retv = -EINVAL;
+
+               /* check PUBLIC/TMP/PUBTMP_DEFAULT conflicts */
+               switch (val & (IPV6_PREFER_SRC_PUBLIC|
+                              IPV6_PREFER_SRC_TMP|
+                              IPV6_PREFER_SRC_PUBTMP_DEFAULT)) {
+               case IPV6_PREFER_SRC_PUBLIC:
+                       pref |= IPV6_PREFER_SRC_PUBLIC;
+                       break;
+               case IPV6_PREFER_SRC_TMP:
+                       pref |= IPV6_PREFER_SRC_TMP;
+                       break;
+               case IPV6_PREFER_SRC_PUBTMP_DEFAULT:
+                       break;
+               case 0:
+                       goto pref_skip_pubtmp;
+               default:
+                       goto e_inval;
+               }
+
+               prefmask &= ~(IPV6_PREFER_SRC_PUBLIC|
+                             IPV6_PREFER_SRC_TMP);
+pref_skip_pubtmp:
+
+               /* check HOME/COA conflicts */
+               switch (val & (IPV6_PREFER_SRC_HOME|IPV6_PREFER_SRC_COA)) {
+               case IPV6_PREFER_SRC_HOME:
+                       break;
+               case IPV6_PREFER_SRC_COA:
+                       pref |= IPV6_PREFER_SRC_COA;
+               case 0:
+                       goto pref_skip_coa;
+               default:
+                       goto e_inval;
+               }
+
+               prefmask &= ~IPV6_PREFER_SRC_COA;
+pref_skip_coa:
+
+               /* check CGA/NONCGA conflicts */
+               switch (val & (IPV6_PREFER_SRC_CGA|IPV6_PREFER_SRC_NONCGA)) {
+               case IPV6_PREFER_SRC_CGA:
+               case IPV6_PREFER_SRC_NONCGA:
+               case 0:
+                       break;
+               default:
+                       goto e_inval;
+               }
+
+               np->srcprefs = (np->srcprefs & prefmask) | pref;
+               retv = 0;
+
+               break;
+           }
        }
+
        release_sock(sk);
 
        return retv;
@@ -728,6 +851,9 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
        int len;
        int val;
 
+       if (ip6_mroute_opt(optname))
+               return ip6_mroute_getsockopt(sk, optname, optval, optlen);
+
        if (get_user(len, optlen))
                return -EFAULT;
        switch (optname) {
@@ -904,9 +1030,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                dst = sk_dst_get(sk);
                if (dst) {
                        if (val < 0)
-                               val = dst_metric(dst, RTAX_HOPLIMIT);
-                       if (val < 0)
-                               val = ipv6_get_hoplimit(dst->dev);
+                               val = ip6_dst_hoplimit(dst);
                        dst_release(dst);
                }
                if (val < 0)
@@ -934,6 +1058,24 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                val = np->sndflow;
                break;
 
+       case IPV6_ADDR_PREFERENCES:
+               val = 0;
+
+               if (np->srcprefs & IPV6_PREFER_SRC_TMP)
+                       val |= IPV6_PREFER_SRC_TMP;
+               else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC)
+                       val |= IPV6_PREFER_SRC_PUBLIC;
+               else {
+                       /* XXX: should we return system default? */
+                       val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT;
+               }
+
+               if (np->srcprefs & IPV6_PREFER_SRC_COA)
+                       val |= IPV6_PREFER_SRC_COA;
+               else
+                       val |= IPV6_PREFER_SRC_HOME;
+               break;
+
        default:
                return -ENOPROTOOPT;
        }