ipv4: tcp: handle ICMP messages on TCP_NEW_SYN_RECV request sockets
authorEric Dumazet <edumazet@google.com>
Sun, 22 Mar 2015 17:22:22 +0000 (10:22 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 23 Mar 2015 20:52:26 +0000 (16:52 -0400)
tcp_v4_err() can restrict lookups to ehash table, and not to listeners.

Note this patch creates the infrastructure, but this means that ICMP
messages for request sockets are ignored until complete conversion.

New tcp_req_err() helper is exported so that we can use it in IPv6
in following patch.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/tcp.h
net/ipv4/tcp_ipv4.c

index 1876262..fe60e00 100644 (file)
@@ -447,6 +447,7 @@ const u8 *tcp_parse_md5sig_option(const struct tcphdr *th);
 
 void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb);
 void tcp_v4_mtu_reduced(struct sock *sk);
+void tcp_req_err(struct sock *sk, u32 seq);
 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb);
 struct sock *tcp_create_openreq_child(struct sock *sk,
                                      struct request_sock *req,
index 8028ad5..a576150 100644 (file)
@@ -310,6 +310,34 @@ static void do_redirect(struct sk_buff *skb, struct sock *sk)
                dst->ops->redirect(dst, sk, skb);
 }
 
+
+/* handle ICMP messages on TCP_NEW_SYN_RECV request sockets */
+void tcp_req_err(struct sock *sk, u32 seq)
+{
+       struct request_sock *req = inet_reqsk(sk);
+       struct net *net = sock_net(sk);
+
+       /* ICMPs are not backlogged, hence we cannot get
+        * an established socket here.
+        */
+       WARN_ON(req->sk);
+
+       if (seq != tcp_rsk(req)->snt_isn) {
+               NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
+       } else {
+               /*
+                * Still in SYN_RECV, just remove it silently.
+                * There is no good way to pass the error to the newly
+                * created socket, and POSIX does not want network
+                * errors returned from accept().
+                */
+               inet_csk_reqsk_queue_drop(req->rsk_listener, req);
+               NET_INC_STATS_BH(net, LINUX_MIB_LISTENDROPS);
+       }
+       reqsk_put(req);
+}
+EXPORT_SYMBOL(tcp_req_err);
+
 /*
  * This routine is called by the ICMP module when it gets some
  * sort of error condition.  If err < 0 then the socket should
@@ -343,8 +371,9 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
        int err;
        struct net *net = dev_net(icmp_skb->dev);
 
-       sk = inet_lookup(net, &tcp_hashinfo, iph->daddr, th->dest,
-                       iph->saddr, th->source, inet_iif(icmp_skb));
+       sk = __inet_lookup_established(net, &tcp_hashinfo, iph->daddr,
+                                      th->dest, iph->saddr, ntohs(th->source),
+                                      inet_iif(icmp_skb));
        if (!sk) {
                ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
                return;
@@ -353,6 +382,9 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
                inet_twsk_put(inet_twsk(sk));
                return;
        }
+       seq = ntohl(th->seq);
+       if (sk->sk_state == TCP_NEW_SYN_RECV)
+               return tcp_req_err(sk, seq);
 
        bh_lock_sock(sk);
        /* If too many ICMPs get dropped on busy
@@ -374,7 +406,6 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
 
        icsk = inet_csk(sk);
        tp = tcp_sk(sk);
-       seq = ntohl(th->seq);
        /* XXX (TFO) - tp->snd_una should be ISN (tcp_create_openreq_child() */
        fastopen = tp->fastopen_rsk;
        snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una;
@@ -458,38 +489,6 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
        }
 
        switch (sk->sk_state) {
-               struct request_sock *req;
-       case TCP_LISTEN:
-               if (sock_owned_by_user(sk))
-                       goto out;
-
-               req = inet_csk_search_req(sk, th->dest,
-                                         iph->daddr, iph->saddr);
-               if (!req)
-                       goto out;
-
-               /* ICMPs are not backlogged, hence we cannot get
-                  an established socket here.
-                */
-               WARN_ON(req->sk);
-
-               if (seq != tcp_rsk(req)->snt_isn) {
-                       NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
-                       reqsk_put(req);
-                       goto out;
-               }
-
-               /*
-                * Still in SYN_RECV, just remove it silently.
-                * There is no good way to pass the error to the newly
-                * created socket, and POSIX does not want network
-                * errors returned from accept().
-                */
-               inet_csk_reqsk_queue_drop(sk, req);
-               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
-               reqsk_put(req);
-               goto out;
-
        case TCP_SYN_SENT:
        case TCP_SYN_RECV:
                /* Only in fast or simultaneous open. If a fast open socket is