These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / dccp / ipv6.c
index 5165571..b8608b7 100644 (file)
@@ -181,7 +181,7 @@ out:
 }
 
 
-static int dccp_v6_send_response(struct sock *sk, struct request_sock *req)
+static int dccp_v6_send_response(const struct sock *sk, struct request_sock *req)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
@@ -202,7 +202,9 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req)
        security_req_classify_flow(req, 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);
        if (IS_ERR(dst)) {
@@ -219,7 +221,10 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req)
                                                         &ireq->ir_v6_loc_addr,
                                                         &ireq->ir_v6_rmt_addr);
                fl6.daddr = ireq->ir_v6_rmt_addr;
-               err = ip6_xmit(sk, skb, &fl6, np->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);
        }
 
@@ -234,7 +239,7 @@ static void dccp_v6_reqsk_destructor(struct request_sock *req)
        kfree_skb(inet_rsk(req)->pktopts);
 }
 
-static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb)
+static void dccp_v6_ctl_send_reset(const struct sock *sk, struct sk_buff *rxskb)
 {
        const struct ipv6hdr *rxip6h;
        struct sk_buff *skb;
@@ -290,37 +295,6 @@ static struct request_sock_ops dccp6_request_sock_ops = {
        .syn_ack_timeout = dccp_syn_ack_timeout,
 };
 
-static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
-{
-       const struct dccp_hdr *dh = dccp_hdr(skb);
-       const struct ipv6hdr *iph = ipv6_hdr(skb);
-       struct request_sock *req;
-       struct sock *nsk;
-
-       req = inet6_csk_search_req(sk, dh->dccph_sport, &iph->saddr,
-                                  &iph->daddr, inet6_iif(skb));
-       if (req) {
-               nsk = dccp_check_req(sk, skb, req);
-               if (!nsk)
-                       reqsk_put(req);
-               return nsk;
-       }
-       nsk = __inet6_lookup_established(sock_net(sk), &dccp_hashinfo,
-                                        &iph->saddr, dh->dccph_sport,
-                                        &iph->daddr, ntohs(dh->dccph_dport),
-                                        inet6_iif(skb));
-       if (nsk != NULL) {
-               if (nsk->sk_state != DCCP_TIME_WAIT) {
-                       bh_lock_sock(nsk);
-                       return nsk;
-               }
-               inet_twsk_put(inet_twsk(nsk));
-               return NULL;
-       }
-
-       return sk;
-}
-
 static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 {
        struct request_sock *req;
@@ -350,7 +324,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
                goto drop;
 
-       req = inet_reqsk_alloc(&dccp6_request_sock_ops, sk);
+       req = inet_reqsk_alloc(&dccp6_request_sock_ops, sk, true);
        if (req == NULL)
                goto drop;
 
@@ -398,7 +372,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        if (dccp_v6_send_response(sk, req))
                goto drop_and_free;
 
-       inet6_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
+       inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
        return 0;
 
 drop_and_free:
@@ -408,13 +382,17 @@ drop:
        return -1;
 }
 
-static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
+static struct sock *dccp_v6_request_recv_sock(const struct sock *sk,
                                              struct sk_buff *skb,
                                              struct request_sock *req,
-                                             struct dst_entry *dst)
+                                             struct dst_entry *dst,
+                                             struct request_sock *req_unhash,
+                                             bool *own_req)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
-       struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
+       struct ipv6_pinfo *newnp;
+       const struct ipv6_pinfo *np = inet6_sk(sk);
+       struct ipv6_txoptions *opt;
        struct inet_sock *newinet;
        struct dccp6_sock *newdp6;
        struct sock *newsk;
@@ -423,7 +401,8 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
                /*
                 *      v6 mapped
                 */
-               newsk = dccp_v4_request_recv_sock(sk, skb, req, dst);
+               newsk = dccp_v4_request_recv_sock(sk, skb, req, dst,
+                                                 req_unhash, own_req);
                if (newsk == NULL)
                        return NULL;
 
@@ -462,22 +441,11 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
        if (sk_acceptq_is_full(sk))
                goto out_overflow;
 
-       if (dst == NULL) {
-               struct in6_addr *final_p, final;
+       if (!dst) {
                struct flowi6 fl6;
 
-               memset(&fl6, 0, sizeof(fl6));
-               fl6.flowi6_proto = IPPROTO_DCCP;
-               fl6.daddr = ireq->ir_v6_rmt_addr;
-               final_p = fl6_update_dst(&fl6, np->opt, &final);
-               fl6.saddr = ireq->ir_v6_loc_addr;
-               fl6.flowi6_oif = sk->sk_bound_dev_if;
-               fl6.fl6_dport = ireq->ir_rmt_port;
-               fl6.fl6_sport = htons(ireq->ir_num);
-               security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
-
-               dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
-               if (IS_ERR(dst))
+               dst = inet6_csk_route_req(sk, &fl6, req, IPPROTO_DCCP);
+               if (!dst)
                        goto out;
        }
 
@@ -491,7 +459,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
         * comment in that function for the gory details. -acme
         */
 
-       __ip6_dst_store(newsk, dst, NULL, NULL);
+       ip6_dst_store(newsk, dst, NULL, NULL);
        newsk->sk_route_caps = dst->dev->features & ~(NETIF_F_IP_CSUM |
                                                      NETIF_F_TSO);
        newdp6 = (struct dccp6_sock *)newsk;
@@ -515,15 +483,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
        /* Clone RX bits */
        newnp->rxopt.all = np->rxopt.all;
 
-       /* Clone pktoptions received with SYN */
        newnp->pktoptions = NULL;
-       if (ireq->pktopts != NULL) {
-               newnp->pktoptions = skb_clone(ireq->pktopts, GFP_ATOMIC);
-               consume_skb(ireq->pktopts);
-               ireq->pktopts = NULL;
-               if (newnp->pktoptions)
-                       skb_set_owner_r(newnp->pktoptions, newsk);
-       }
        newnp->opt        = NULL;
        newnp->mcast_oif  = inet6_iif(skb);
        newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
@@ -534,13 +494,15 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
         * Yes, keeping reference count would be much more clever, but we make
         * one more one thing there: reattach optmem to newsk.
         */
-       if (np->opt != NULL)
-               newnp->opt = ipv6_dup_options(newsk, np->opt);
-
+       opt = rcu_dereference(np->opt);
+       if (opt) {
+               opt = ipv6_dup_options(newsk, opt);
+               RCU_INIT_POINTER(newnp->opt, opt);
+       }
        inet_csk(newsk)->icsk_ext_hdr_len = 0;
-       if (newnp->opt != NULL)
-               inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
-                                                    newnp->opt->opt_flen);
+       if (opt)
+               inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen +
+                                                   opt->opt_flen;
 
        dccp_sync_mss(newsk, dst_mtu(dst));
 
@@ -552,7 +514,15 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
                dccp_done(newsk);
                goto out;
        }
-       __inet_hash(newsk, NULL);
+       *own_req = inet_ehash_nolisten(newsk, req_to_sk(req_unhash));
+       /* Clone pktoptions received with SYN, if we own the req */
+       if (*own_req && ireq->pktopts) {
+               newnp->pktoptions = skb_clone(ireq->pktopts, GFP_ATOMIC);
+               consume_skb(ireq->pktopts);
+               ireq->pktopts = NULL;
+               if (newnp->pktoptions)
+                       skb_set_owner_r(newnp->pktoptions, newsk);
+       }
 
        return newsk;
 
@@ -651,24 +621,6 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
         * NOTE: the check for the packet types is done in
         *       dccp_rcv_state_process
         */
-       if (sk->sk_state == DCCP_LISTEN) {
-               struct sock *nsk = dccp_v6_hnd_req(sk, skb);
-
-               if (nsk == NULL)
-                       goto discard;
-               /*
-                * Queue it on the new socket if the new socket is active,
-                * otherwise we just shortcircuit this and continue with
-                * the new socket..
-                */
-               if (nsk != sk) {
-                       if (dccp_child_process(sk, nsk, skb))
-                               goto reset;
-                       if (opt_skb != NULL)
-                               __kfree_skb(opt_skb);
-                       return 0;
-               }
-       }
 
        if (dccp_rcv_state_process(sk, skb, dccp_hdr(skb), skb->len))
                goto reset;
@@ -715,16 +667,11 @@ static int dccp_v6_rcv(struct sk_buff *skb)
        else
                DCCP_SKB_CB(skb)->dccpd_ack_seq = dccp_hdr_ack_seq(skb);
 
-       /* Step 2:
-        *      Look up flow ID in table and get corresponding socket */
+lookup:
        sk = __inet6_lookup_skb(&dccp_hashinfo, skb,
                                dh->dccph_sport, dh->dccph_dport,
                                inet6_iif(skb));
-       /*
-        * Step 2:
-        *      If no socket ...
-        */
-       if (sk == NULL) {
+       if (!sk) {
                dccp_pr_debug("failed to look up flow ID in table and "
                              "get corresponding socket\n");
                goto no_dccp_socket;
@@ -742,6 +689,31 @@ static int dccp_v6_rcv(struct sk_buff *skb)
                goto no_dccp_socket;
        }
 
+       if (sk->sk_state == DCCP_NEW_SYN_RECV) {
+               struct request_sock *req = inet_reqsk(sk);
+               struct sock *nsk;
+
+               sk = req->rsk_listener;
+               if (unlikely(sk->sk_state != DCCP_LISTEN)) {
+                       inet_csk_reqsk_queue_drop_and_put(sk, req);
+                       goto lookup;
+               }
+               sock_hold(sk);
+               nsk = dccp_check_req(sk, skb, req);
+               if (!nsk) {
+                       reqsk_put(req);
+                       goto discard_and_relse;
+               }
+               if (nsk == sk) {
+                       reqsk_put(req);
+               } else if (dccp_child_process(sk, nsk, skb)) {
+                       dccp_v6_ctl_send_reset(sk, skb);
+                       goto discard_and_relse;
+               } else {
+                       sock_put(sk);
+                       return 0;
+               }
+       }
        /*
         * RFC 4340, sec. 9.2.1: Minimum Checksum Coverage
         *      o if MinCsCov = 0, only packets with CsCov = 0 are accepted
@@ -793,6 +765,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct dccp_sock *dp = dccp_sk(sk);
        struct in6_addr *saddr = NULL, *final_p, final;
+       struct ipv6_txoptions *opt;
        struct flowi6 fl6;
        struct dst_entry *dst;
        int addr_type;
@@ -892,7 +865,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        fl6.fl6_sport = inet->inet_sport;
        security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
 
-       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);
 
        dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
        if (IS_ERR(dst)) {
@@ -909,12 +883,11 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        np->saddr = *saddr;
        inet->inet_rcv_saddr = LOOPBACK4_IPV6;
 
-       __ip6_dst_store(sk, dst, NULL, NULL);
+       ip6_dst_store(sk, dst, NULL, NULL);
 
        icsk->icsk_ext_hdr_len = 0;
-       if (np->opt != NULL)
-               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;
 
        inet->inet_dport = usin->sin6_port;