]> Pileus Git - ~andy/linux/blobdiff - net/ipv6/ipv6_sockglue.c
[NET]: Fix IP_ADD/DROP_MEMBERSHIP to handle only connectionless
[~andy/linux] / net / ipv6 / ipv6_sockglue.c
index f5f9582a8d39554c120ed147ddc9f71105d6a889..6b038aa72e88b51fe77554bc4cab65af072ef2e8 100644 (file)
@@ -101,14 +101,14 @@ static int ipv6_gso_send_check(struct sk_buff *skb)
        if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
                goto out;
 
-       ipv6h = skb->nh.ipv6h;
+       ipv6h = ipv6_hdr(skb);
        __skb_pull(skb, sizeof(*ipv6h));
        err = -EPROTONOSUPPORT;
 
        rcu_read_lock();
        ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
        if (likely(ops && ops->gso_send_check)) {
-               skb->h.raw = skb->data;
+               skb_reset_transport_header(skb);
                err = ops->gso_send_check(skb);
        }
        rcu_read_unlock();
@@ -123,7 +123,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
        struct ipv6hdr *ipv6h;
        struct inet6_protocol *ops;
 
-       if (!(features & NETIF_F_HW_CSUM))
+       if (!(features & NETIF_F_V6_CSUM))
                features &= ~NETIF_F_SG;
 
        if (unlikely(skb_shinfo(skb)->gso_type &
@@ -137,14 +137,14 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
        if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
                goto out;
 
-       ipv6h = skb->nh.ipv6h;
+       ipv6h = ipv6_hdr(skb);
        __skb_pull(skb, sizeof(*ipv6h));
        segs = ERR_PTR(-EPROTONOSUPPORT);
 
        rcu_read_lock();
        ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
        if (likely(ops && ops->gso_segment)) {
-               skb->h.raw = skb->data;
+               skb_reset_transport_header(skb);
                segs = ops->gso_segment(skb, features);
        }
        rcu_read_unlock();
@@ -153,7 +153,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
                goto out;
 
        for (skb = segs; skb; skb = skb->next) {
-               ipv6h = skb->nh.ipv6h;
+               ipv6h = ipv6_hdr(skb);
                ipv6h->payload_len = htons(skb->len - skb->mac_len -
                                           sizeof(*ipv6h));
        }
@@ -336,16 +336,12 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                break;
 
        case IPV6_RECVRTHDR:
-               if (val < 0 || val > 2)
-                       goto e_inval;
-               np->rxopt.bits.srcrt = val;
+               np->rxopt.bits.srcrt = valbool;
                retv = 0;
                break;
 
        case IPV6_2292RTHDR:
-               if (val < 0 || val > 2)
-                       goto e_inval;
-               np->rxopt.bits.osrcrt = val;
+               np->rxopt.bits.osrcrt = valbool;
                retv = 0;
                break;
 
@@ -416,11 +412,10 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                if (optname == IPV6_RTHDR && opt && opt->srcrt) {
                        struct ipv6_rt_hdr *rthdr = opt->srcrt;
                        switch (rthdr->type) {
-                       case IPV6_SRCRT_TYPE_0:
-#ifdef CONFIG_IPV6_MIP6
+#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
                        case IPV6_SRCRT_TYPE_2:
-#endif
                                break;
+#endif
                        default:
                                goto sticky_done;
                        }
@@ -559,6 +554,10 @@ done:
        {
                struct ipv6_mreq mreq;
 
+               retv = -EPROTO;
+               if (inet_sk(sk)->is_icsk)
+                       break;
+
                retv = -EFAULT;
                if (copy_from_user(&mreq, optval, sizeof(struct ipv6_mreq)))
                        break;
@@ -694,7 +693,7 @@ done:
                retv = ip6_ra_control(sk, val, NULL);
                break;
        case IPV6_MTU_DISCOVER:
-               if (val<0 || val>2)
+               if (val<0 || val>3)
                        goto e_inval;
                np->pmtudisc = val;
                retv = 0;
@@ -761,6 +760,7 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
        return err;
 }
 
+EXPORT_SYMBOL(ipv6_setsockopt);
 
 #ifdef CONFIG_COMPAT
 int compat_ipv6_setsockopt(struct sock *sk, int level, int optname,
@@ -796,18 +796,37 @@ EXPORT_SYMBOL(compat_ipv6_setsockopt);
 #endif
 
 static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
-                                 char __user *optval, int len)
+                                 int optname, char __user *optval, int len)
 {
        struct ipv6_opt_hdr *hdr;
 
-       if (!opt || !opt->hopopt)
+       if (!opt)
+               return 0;
+
+       switch(optname) {
+       case IPV6_HOPOPTS:
+               hdr = opt->hopopt;
+               break;
+       case IPV6_RTHDRDSTOPTS:
+               hdr = opt->dst0opt;
+               break;
+       case IPV6_RTHDR:
+               hdr = (struct ipv6_opt_hdr *)opt->srcrt;
+               break;
+       case IPV6_DSTOPTS:
+               hdr = opt->dst1opt;
+               break;
+       default:
+               return -EINVAL; /* should not happen */
+       }
+
+       if (!hdr)
                return 0;
-       hdr = opt->hopopt;
 
        len = min_t(unsigned int, len, ipv6_optlen(hdr));
-       if (copy_to_user(optval, hdr, ipv6_optlen(hdr)))
+       if (copy_to_user(optval, hdr, len))
                return -EFAULT;
-       return len;
+       return ipv6_optlen(hdr);
 }
 
 static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
@@ -945,7 +964,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
 
                lock_sock(sk);
                len = ipv6_getsockopt_sticky(sk, np->opt,
-                                            optval, len);
+                                            optname, optval, len);
                release_sock(sk);
                return put_user(len, optlen);
        }
@@ -1066,6 +1085,8 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname,
        return err;
 }
 
+EXPORT_SYMBOL(ipv6_getsockopt);
+
 #ifdef CONFIG_COMPAT
 int compat_ipv6_getsockopt(struct sock *sk, int level, int optname,
                           char __user *optval, int __user *optlen)