Linux 3.2.88
[pandora-kernel.git] / net / ipv6 / tcp_ipv6.c
index 655cc60..2c2d711 100644 (file)
@@ -131,6 +131,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
        struct in6_addr *saddr = NULL, *final_p, final;
+       struct ipv6_txoptions *opt;
        struct rt6_info *rt;
        struct flowi6 fl6;
        struct dst_entry *dst;
@@ -252,7 +253,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        fl6.fl6_dport = usin->sin6_port;
        fl6.fl6_sport = inet->inet_sport;
 
-       final_p = fl6_update_dst(&fl6, np->opt, &final);
+       opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk));
+       final_p = fl6_update_dst(&fl6, opt, &final);
 
        security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
 
@@ -295,9 +297,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        }
 
        icsk->icsk_ext_hdr_len = 0;
-       if (np->opt)
-               icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
-                                         np->opt->opt_nflen);
+       if (opt)
+               icsk->icsk_ext_hdr_len = opt->opt_flen +
+                                        opt->opt_nflen;
 
        tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
 
@@ -481,7 +483,6 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
        struct inet6_request_sock *treq = inet6_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct sk_buff * skb;
-       struct ipv6_txoptions *opt = NULL;
        struct in6_addr * final_p, final;
        struct flowi6 fl6;
        struct dst_entry *dst;
@@ -498,8 +499,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
        fl6.fl6_sport = inet_rsk(req)->loc_port;
        security_req_classify_flow(req, flowi6_to_flowi(&fl6));
 
-       opt = np->opt;
-       final_p = fl6_update_dst(&fl6, opt, &final);
+       final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final);
 
        dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false);
        if (IS_ERR(dst)) {
@@ -513,13 +513,14 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
                __tcp_v6_send_check(skb, &treq->loc_addr, &treq->rmt_addr);
 
                ipv6_addr_copy(&fl6.daddr, &treq->rmt_addr);
-               err = ip6_xmit(sk, skb, &fl6, opt, np->tclass);
+               rcu_read_lock();
+               err = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt),
+                              np->tclass);
+               rcu_read_unlock();
                err = net_xmit_eval(err);
        }
 
 done:
-       if (opt && opt != np->opt)
-               sock_kfree_s(sk, opt, opt->tot_len);
        dst_release(dst);
        return err;
 }
@@ -1408,7 +1409,6 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        }
 
        treq = inet6_rsk(req);
-       opt = np->opt;
 
        if (sk_acceptq_is_full(sk))
                goto out_overflow;
@@ -1476,16 +1476,15 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
           but we make one more one thing there: reattach optmem
           to newsk.
         */
+       opt = rcu_dereference(np->opt);
        if (opt) {
-               newnp->opt = ipv6_dup_options(newsk, opt);
-               if (opt != np->opt)
-                       sock_kfree_s(sk, opt, opt->tot_len);
+               opt = ipv6_dup_options(newsk, opt);
+               RCU_INIT_POINTER(newnp->opt, opt);
        }
-
-       inet_csk(newsk)->icsk_ext_hdr_len = 0;
-       if (newnp->opt)
-               inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
-                                                    newnp->opt->opt_flen);
+       inet_csk(newsk)->icsk_ext_hdr_len = 0;
+       if (opt)
+               inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen +
+                                                   opt->opt_flen;
 
        tcp_mtup_init(newsk);
        tcp_sync_mss(newsk, dst_mtu(dst));
@@ -1530,8 +1529,6 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
 out_overflow:
        NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
 out_nonewsk:
-       if (opt && opt != np->opt)
-               sock_kfree_s(sk, opt, opt->tot_len);
        dst_release(dst);
 out:
        NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
@@ -1588,7 +1585,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
                goto discard;
 #endif
 
-       if (sk_filter(sk, skb))
+       if (tcp_filter(sk, skb))
                goto discard;
 
        /*
@@ -1746,8 +1743,10 @@ process:
        if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
                goto discard_and_relse;
 
-       if (sk_filter(sk, skb))
+       if (tcp_filter(sk, skb))
                goto discard_and_relse;
+       th = (const struct tcphdr *)skb->data;
+       hdr = ipv6_hdr(skb);
 
        skb->dev = NULL;