Merge tag 'for-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
[firefly-linux-kernel-4.4.55.git] / net / ipv6 / tcp_ipv6.c
index 9e96b5f21d2ae1d5c3a0092459afbc156b1b19d7..f49476e2d8845092d8d0e30dcf6ec9a7db7a6106 100644 (file)
@@ -315,6 +315,23 @@ failure:
        return err;
 }
 
+static void tcp_v6_mtu_reduced(struct sock *sk)
+{
+       struct dst_entry *dst;
+
+       if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))
+               return;
+
+       dst = inet6_csk_update_pmtu(sk, tcp_sk(sk)->mtu_info);
+       if (!dst)
+               return;
+
+       if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst)) {
+               tcp_sync_mss(sk, dst_mtu(dst));
+               tcp_simple_retransmit(sk);
+       }
+}
+
 static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                u8 type, u8 code, int offset, __be32 info)
 {
@@ -342,7 +359,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        }
 
        bh_lock_sock(sk);
-       if (sock_owned_by_user(sk))
+       if (sock_owned_by_user(sk) && type != ICMPV6_PKT_TOOBIG)
                NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS);
 
        if (sk->sk_state == TCP_CLOSE)
@@ -363,51 +380,19 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 
        np = inet6_sk(sk);
 
-       if (type == ICMPV6_PKT_TOOBIG) {
-               struct dst_entry *dst;
-
-               if (sock_owned_by_user(sk))
-                       goto out;
-               if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))
-                       goto out;
-
-               /* icmp should have updated the destination cache entry */
-               dst = __sk_dst_check(sk, np->dst_cookie);
+       if (type == NDISC_REDIRECT) {
+               struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
 
-               if (dst == NULL) {
-                       struct inet_sock *inet = inet_sk(sk);
-                       struct flowi6 fl6;
-
-                       /* BUGGG_FUTURE: Again, it is not clear how
-                          to handle rthdr case. Ignore this complexity
-                          for now.
-                        */
-                       memset(&fl6, 0, sizeof(fl6));
-                       fl6.flowi6_proto = IPPROTO_TCP;
-                       fl6.daddr = np->daddr;
-                       fl6.saddr = np->saddr;
-                       fl6.flowi6_oif = sk->sk_bound_dev_if;
-                       fl6.flowi6_mark = sk->sk_mark;
-                       fl6.fl6_dport = inet->inet_dport;
-                       fl6.fl6_sport = inet->inet_sport;
-                       security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
-
-                       dst = ip6_dst_lookup_flow(sk, &fl6, NULL, false);
-                       if (IS_ERR(dst)) {
-                               sk->sk_err_soft = -PTR_ERR(dst);
-                               goto out;
-                       }
-
-               } else
-                       dst_hold(dst);
-
-               dst->ops->update_pmtu(dst, ntohl(info));
+               if (dst)
+                       dst->ops->redirect(dst, sk, skb);
+       }
 
-               if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst)) {
-                       tcp_sync_mss(sk, dst_mtu(dst));
-                       tcp_simple_retransmit(sk);
-               } /* else let the usual retransmit timer handle it */
-               dst_release(dst);
+       if (type == ICMPV6_PKT_TOOBIG) {
+               tp->mtu_info = ntohl(info);
+               if (!sock_owned_by_user(sk))
+                       tcp_v6_mtu_reduced(sk);
+               else
+                       set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags);
                goto out;
        }
 
@@ -1055,7 +1040,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        tcp_clear_options(&tmp_opt);
        tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
        tmp_opt.user_mss = tp->rx_opt.user_mss;
-       tcp_parse_options(skb, &tmp_opt, &hash_location, 0);
+       tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL);
 
        if (tmp_opt.cookie_plus > 0 &&
            tmp_opt.saw_tstamp &&
@@ -1689,20 +1674,6 @@ do_time_wait:
        goto discard_it;
 }
 
-static struct inet_peer *tcp_v6_get_peer(struct sock *sk)
-{
-       struct rt6_info *rt = (struct rt6_info *) __sk_dst_get(sk);
-       struct ipv6_pinfo *np = inet6_sk(sk);
-
-       /* If we don't have a valid cached route, or we're doing IP
-        * options which make the IPv6 header destination address
-        * different from our peer's, do not bother with this.
-        */
-       if (!rt || !ipv6_addr_equal(&np->daddr, &rt->rt6i_dst.addr))
-               return NULL;
-       return rt6_get_peer_create(rt);
-}
-
 static struct timewait_sock_ops tcp6_timewait_sock_ops = {
        .twsk_obj_size  = sizeof(struct tcp6_timewait_sock),
        .twsk_unique    = tcp_twsk_unique,
@@ -1715,7 +1686,6 @@ static const struct inet_connection_sock_af_ops ipv6_specific = {
        .rebuild_header    = inet6_sk_rebuild_header,
        .conn_request      = tcp_v6_conn_request,
        .syn_recv_sock     = tcp_v6_syn_recv_sock,
-       .get_peer          = tcp_v6_get_peer,
        .net_header_len    = sizeof(struct ipv6hdr),
        .net_frag_header_len = sizeof(struct frag_hdr),
        .setsockopt        = ipv6_setsockopt,
@@ -1747,7 +1717,6 @@ static const struct inet_connection_sock_af_ops ipv6_mapped = {
        .rebuild_header    = inet_sk_rebuild_header,
        .conn_request      = tcp_v6_conn_request,
        .syn_recv_sock     = tcp_v6_syn_recv_sock,
-       .get_peer          = tcp_v4_get_peer,
        .net_header_len    = sizeof(struct iphdr),
        .setsockopt        = ipv6_setsockopt,
        .getsockopt        = ipv6_getsockopt,
@@ -1986,6 +1955,8 @@ struct proto tcpv6_prot = {
        .sendmsg                = tcp_sendmsg,
        .sendpage               = tcp_sendpage,
        .backlog_rcv            = tcp_v6_do_rcv,
+       .release_cb             = tcp_release_cb,
+       .mtu_reduced            = tcp_v6_mtu_reduced,
        .hash                   = tcp_v6_hash,
        .unhash                 = inet_unhash,
        .get_port               = inet_csk_get_port,