]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - net/ipv6/tcp_ipv6.c
Merge branch 'stable-4.8' of git://git.infradead.org/users/pcmoore/selinux into next
[mirror_ubuntu-artful-kernel.git] / net / ipv6 / tcp_ipv6.c
index 18daddc6001c5063a2223f0fc2d90658ce6813f7..818b3b5cd490cbacf1fae9163cab967263fc60a2 100644 (file)
@@ -234,7 +234,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        fl6.fl6_dport = usin->sin6_port;
        fl6.fl6_sport = inet->inet_sport;
 
-       opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk));
+       opt = rcu_dereference_protected(np->opt, lockdep_sock_is_held(sk));
        final_p = fl6_update_dst(&fl6, opt, &final);
 
        security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
@@ -336,8 +336,8 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                                        skb->dev->ifindex);
 
        if (!sk) {
-               ICMP6_INC_STATS_BH(net, __in6_dev_get(skb->dev),
-                                  ICMP6_MIB_INERRORS);
+               __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev),
+                                 ICMP6_MIB_INERRORS);
                return;
        }
 
@@ -352,13 +352,13 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 
        bh_lock_sock(sk);
        if (sock_owned_by_user(sk) && type != ICMPV6_PKT_TOOBIG)
-               NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS);
+               __NET_INC_STATS(net, LINUX_MIB_LOCKDROPPEDICMPS);
 
        if (sk->sk_state == TCP_CLOSE)
                goto out;
 
        if (ipv6_hdr(skb)->hop_limit < inet6_sk(sk)->min_hopcount) {
-               NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
+               __NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP);
                goto out;
        }
 
@@ -368,7 +368,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una;
        if (sk->sk_state != TCP_LISTEN &&
            !between(seq, snd_una, tp->snd_nxt)) {
-               NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
+               __NET_INC_STATS(net, LINUX_MIB_OUTOFWINDOWICMPS);
                goto out;
        }
 
@@ -439,7 +439,7 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst,
                              struct flowi *fl,
                              struct request_sock *req,
                              struct tcp_fastopen_cookie *foc,
-                             bool attach_req)
+                             enum tcp_synack_type synack_type)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
@@ -453,7 +453,7 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst,
                                               IPPROTO_TCP)) == NULL)
                goto done;
 
-       skb = tcp_make_synack(sk, dst, req, foc, attach_req);
+       skb = tcp_make_synack(sk, dst, req, foc, synack_type);
 
        if (skb) {
                __tcp_v6_send_check(skb, &ireq->ir_v6_loc_addr,
@@ -653,12 +653,12 @@ static bool tcp_v6_inbound_md5_hash(const struct sock *sk,
                return false;
 
        if (hash_expected && !hash_location) {
-               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND);
+               NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND);
                return true;
        }
 
        if (!hash_expected && hash_location) {
-               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED);
+               NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED);
                return true;
        }
 
@@ -814,8 +814,13 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32
        fl6.flowi6_proto = IPPROTO_TCP;
        if (rt6_need_strict(&fl6.daddr) && !oif)
                fl6.flowi6_oif = tcp_v6_iif(skb);
-       else
+       else {
+               if (!oif && netif_index_is_l3_master(net, skb->skb_iif))
+                       oif = skb->skb_iif;
+
                fl6.flowi6_oif = oif;
+       }
+
        fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark);
        fl6.fl6_dport = t1->dest;
        fl6.fl6_sport = t1->source;
@@ -829,9 +834,9 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32
        if (!IS_ERR(dst)) {
                skb_dst_set(buff, dst);
                ip6_xmit(ctl_sk, buff, &fl6, NULL, tclass);
-               TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);
+               TCP_INC_STATS(net, TCP_MIB_OUTSEGS);
                if (rst)
-                       TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS);
+                       TCP_INC_STATS(net, TCP_MIB_OUTRSTS);
                return;
        }
 
@@ -862,6 +867,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
                return;
 
 #ifdef CONFIG_TCP_MD5SIG
+       rcu_read_lock();
        hash_location = tcp_parse_md5sig_option(th);
        if (sk && sk_fullsock(sk)) {
                key = tcp_v6_md5_do_lookup(sk, &ipv6h->saddr);
@@ -879,16 +885,15 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
                                           th->source, &ipv6h->daddr,
                                           ntohs(th->source), tcp_v6_iif(skb));
                if (!sk1)
-                       return;
+                       goto out;
 
-               rcu_read_lock();
                key = tcp_v6_md5_do_lookup(sk1, &ipv6h->saddr);
                if (!key)
-                       goto release_sk1;
+                       goto out;
 
                genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, skb);
                if (genhash || memcmp(hash_location, newhash, 16) != 0)
-                       goto release_sk1;
+                       goto out;
        }
 #endif
 
@@ -902,11 +907,8 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
        tcp_v6_send_response(sk, skb, seq, ack_seq, 0, 0, 0, oif, key, 1, 0, 0);
 
 #ifdef CONFIG_TCP_MD5SIG
-release_sk1:
-       if (sk1) {
-               rcu_read_unlock();
-               sock_put(sk1);
-       }
+out:
+       rcu_read_unlock();
 #endif
 }
 
@@ -971,7 +973,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
                                &tcp_request_sock_ipv6_ops, sk, skb);
 
 drop:
-       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
+       tcp_listendrop(sk);
        return 0; /* don't send reset */
 }
 
@@ -1174,11 +1176,11 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
        return newsk;
 
 out_overflow:
-       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
+       __NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
 out_nonewsk:
        dst_release(dst);
 out:
-       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
+       tcp_listendrop(sk);
        return NULL;
 }
 
@@ -1285,8 +1287,8 @@ discard:
        kfree_skb(skb);
        return 0;
 csum_err:
-       TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_CSUMERRORS);
-       TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
+       TCP_INC_STATS(sock_net(sk), TCP_MIB_CSUMERRORS);
+       TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS);
        goto discard;
 
 
@@ -1357,6 +1359,7 @@ static int tcp_v6_rcv(struct sk_buff *skb)
 {
        const struct tcphdr *th;
        const struct ipv6hdr *hdr;
+       bool refcounted;
        struct sock *sk;
        int ret;
        struct net *net = dev_net(skb->dev);
@@ -1367,14 +1370,14 @@ static int tcp_v6_rcv(struct sk_buff *skb)
        /*
         *      Count it even if it's bad.
         */
-       TCP_INC_STATS_BH(net, TCP_MIB_INSEGS);
+       __TCP_INC_STATS(net, TCP_MIB_INSEGS);
 
        if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
                goto discard_it;
 
-       th = tcp_hdr(skb);
+       th = (const struct tcphdr *)skb->data;
 
-       if (th->doff < sizeof(struct tcphdr)/4)
+       if (unlikely(th->doff < sizeof(struct tcphdr)/4))
                goto bad_packet;
        if (!pskb_may_pull(skb, th->doff*4))
                goto discard_it;
@@ -1382,12 +1385,13 @@ static int tcp_v6_rcv(struct sk_buff *skb)
        if (skb_checksum_init(skb, IPPROTO_TCP, ip6_compute_pseudo))
                goto csum_error;
 
-       th = tcp_hdr(skb);
+       th = (const struct tcphdr *)skb->data;
        hdr = ipv6_hdr(skb);
 
 lookup:
        sk = __inet6_lookup_skb(&tcp_hashinfo, skb, __tcp_hdrlen(th),
-                               th->source, th->dest, inet6_iif(skb));
+                               th->source, th->dest, inet6_iif(skb),
+                               &refcounted);
        if (!sk)
                goto no_tcp_socket;
 
@@ -1410,6 +1414,7 @@ process:
                        goto lookup;
                }
                sock_hold(sk);
+               refcounted = true;
                nsk = tcp_check_req(sk, skb, req, false);
                if (!nsk) {
                        reqsk_put(req);
@@ -1427,7 +1432,7 @@ process:
                }
        }
        if (hdr->hop_limit < inet6_sk(sk)->min_hopcount) {
-               NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
+               __NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP);
                goto discard_and_relse;
        }
 
@@ -1460,13 +1465,14 @@ process:
        } else if (unlikely(sk_add_backlog(sk, skb,
                                           sk->sk_rcvbuf + sk->sk_sndbuf))) {
                bh_unlock_sock(sk);
-               NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP);
+               __NET_INC_STATS(net, LINUX_MIB_TCPBACKLOGDROP);
                goto discard_and_relse;
        }
        bh_unlock_sock(sk);
 
 put_and_return:
-       sock_put(sk);
+       if (refcounted)
+               sock_put(sk);
        return ret ? -1 : 0;
 
 no_tcp_socket:
@@ -1477,9 +1483,9 @@ no_tcp_socket:
 
        if (tcp_checksum_complete(skb)) {
 csum_error:
-               TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS);
+               __TCP_INC_STATS(net, TCP_MIB_CSUMERRORS);
 bad_packet:
-               TCP_INC_STATS_BH(net, TCP_MIB_INERRS);
+               __TCP_INC_STATS(net, TCP_MIB_INERRS);
        } else {
                tcp_v6_send_reset(NULL, skb);
        }
@@ -1489,7 +1495,9 @@ discard_it:
        return 0;
 
 discard_and_relse:
-       sock_put(sk);
+       sk_drops_add(sk, skb);
+       if (refcounted)
+               sock_put(sk);
        goto discard_it;
 
 do_time_wait:
@@ -1520,6 +1528,7 @@ do_time_wait:
                        inet_twsk_deschedule_put(tw);
                        sk = sk2;
                        tcp_v6_restore_cb(skb);
+                       refcounted = false;
                        goto process;
                }
                /* Fall through to ACK */