netfilter: conntrack: improve RST handling when tuple is re-used
[linux-2.6-microblaze.git] / net / netfilter / nf_conntrack_proto_tcp.c
index eb4de92..b8ff676 100644 (file)
@@ -823,6 +823,22 @@ static noinline bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb,
        return true;
 }
 
+static bool tcp_can_early_drop(const struct nf_conn *ct)
+{
+       switch (ct->proto.tcp.state) {
+       case TCP_CONNTRACK_FIN_WAIT:
+       case TCP_CONNTRACK_LAST_ACK:
+       case TCP_CONNTRACK_TIME_WAIT:
+       case TCP_CONNTRACK_CLOSE:
+       case TCP_CONNTRACK_CLOSE_WAIT:
+               return true;
+       default:
+               break;
+       }
+
+       return false;
+}
+
 /* Returns verdict for packet, or -1 for invalid. */
 int nf_conntrack_tcp_packet(struct nf_conn *ct,
                            struct sk_buff *skb,
@@ -1030,9 +1046,28 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
                if (index != TCP_RST_SET)
                        break;
 
-               if (ct->proto.tcp.seen[!dir].flags & IP_CT_TCP_FLAG_MAXACK_SET) {
+               /* If we are closing, tuple might have been re-used already.
+                * last_index, last_ack, and all other ct fields used for
+                * sequence/window validation are outdated in that case.
+                *
+                * As the conntrack can already be expired by GC under pressure,
+                * just skip validation checks.
+                */
+               if (tcp_can_early_drop(ct))
+                       goto in_window;
+
+               /* td_maxack might be outdated if we let a SYN through earlier */
+               if ((ct->proto.tcp.seen[!dir].flags & IP_CT_TCP_FLAG_MAXACK_SET) &&
+                   ct->proto.tcp.last_index != TCP_SYN_SET) {
                        u32 seq = ntohl(th->seq);
 
+                       /* If we are not in established state and SEQ=0 this is most
+                        * likely an answer to a SYN we let go through above (last_index
+                        * can be updated due to out-of-order ACKs).
+                        */
+                       if (seq == 0 && !nf_conntrack_tcp_established(ct))
+                               break;
+
                        if (before(seq, ct->proto.tcp.seen[!dir].td_maxack)) {
                                /* Invalid RST  */
                                spin_unlock_bh(&ct->lock);
@@ -1165,22 +1200,6 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
        return NF_ACCEPT;
 }
 
-static bool tcp_can_early_drop(const struct nf_conn *ct)
-{
-       switch (ct->proto.tcp.state) {
-       case TCP_CONNTRACK_FIN_WAIT:
-       case TCP_CONNTRACK_LAST_ACK:
-       case TCP_CONNTRACK_TIME_WAIT:
-       case TCP_CONNTRACK_CLOSE:
-       case TCP_CONNTRACK_CLOSE_WAIT:
-               return true;
-       default:
-               break;
-       }
-
-       return false;
-}
-
 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 
 #include <linux/netfilter/nfnetlink.h>