Linux 3.2.102
[pandora-kernel.git] / net / ipv6 / af_inet6.c
index d27c797..7039068 100644 (file)
@@ -109,6 +109,9 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
        int try_loading_module = 0;
        int err;
 
+       if (protocol < 0 || protocol >= IPPROTO_MAX)
+               return -EINVAL;
+
        if (sock->type != SOCK_RAW &&
            sock->type != SOCK_DGRAM &&
            !inet_ehash_secret)
@@ -430,8 +433,11 @@ void inet6_destroy_sock(struct sock *sk)
 
        /* Free tx options */
 
-       if ((opt = xchg(&np->opt, NULL)) != NULL)
-               sock_kfree_s(sk, opt, opt->tot_len);
+       opt = xchg((__force struct ipv6_txoptions **)&np->opt, NULL);
+       if (opt) {
+               atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
+               txopt_put(opt);
+       }
 }
 
 EXPORT_SYMBOL_GPL(inet6_destroy_sock);
@@ -669,7 +675,10 @@ int inet6_sk_rebuild_header(struct sock *sk)
                fl6.fl6_sport = inet->inet_sport;
                security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
 
-               final_p = fl6_update_dst(&fl6, np->opt, &final);
+               rcu_read_lock();
+               final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt),
+                                        &final);
+               rcu_read_unlock();
 
                dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false);
                if (IS_ERR(dst)) {
@@ -776,7 +785,6 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, u32 features)
        const struct inet6_protocol *ops;
        int proto;
        struct frag_hdr *fptr;
-       unsigned int unfrag_ip6hlen;
        u8 *prevhdr;
        int offset = 0;
 
@@ -815,9 +823,13 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, u32 features)
                ipv6h->payload_len = htons(skb->len - skb->mac_len -
                                           sizeof(*ipv6h));
                if (proto == IPPROTO_UDP) {
-                       unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
+                       int err = ip6_find_1stfragopt(skb, &prevhdr);
+                       if (err < 0) {
+                               kfree_skb_list(segs);
+                               return ERR_PTR(err);
+                       }
                        fptr = (struct frag_hdr *)(skb_network_header(skb) +
-                               unfrag_ip6hlen);
+                               err);
                        fptr->frag_off = htons(offset);
                        if (skb->next != NULL)
                                fptr->frag_off |= htons(IP6_MF);
@@ -870,6 +882,7 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
        ops = rcu_dereference(inet6_protos[proto]);
        if (!ops || !ops->gro_receive) {
                __pskb_pull(skb, skb_gro_offset(skb));
+               skb_gro_frag0_invalidate(skb);
                proto = ipv6_gso_pull_exthdrs(skb, proto);
                skb_gro_pull(skb, -skb_transport_offset(skb));
                skb_reset_transport_header(skb);