]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
stream: go from FIN_WAIT_1 to CLOSING on simultaneous close.
authorVictor Julien <victor@inliniac.net>
Tue, 16 Oct 2012 09:14:59 +0000 (11:14 +0200)
committerVictor Julien <victor@inliniac.net>
Tue, 16 Oct 2012 09:14:59 +0000 (11:14 +0200)
src/stream-tcp.c

index a926832fbf99193084f21649e0fae0c70be90a33..74b07fa63c21d47fc250e31eef3df19d84c474cb 100644 (file)
@@ -2215,7 +2215,7 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p,
                     &ssn->server, p, pq);
         }
 
-    } else if (p->tcph->th_flags & TH_FIN) {
+    } else if ((p->tcph->th_flags & (TH_FIN|TH_ACK)) == (TH_FIN|TH_ACK)) {
         if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
             if (!StreamTcpValidateTimestamp(ssn, p))
                 return -1;
@@ -2323,6 +2323,113 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p,
                     ssn->client.last_ack);
         }
 
+    } else if (p->tcph->th_flags & TH_FIN) {
+        if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+            if (!StreamTcpValidateTimestamp(ssn, p))
+                return -1;
+        }
+
+        if (PKT_IS_TOSERVER(p)) {
+            SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ "
+                    "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+                    TCP_GET_SEQ(p), TCP_GET_ACK(p));
+
+            if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) ||
+                    SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window)))
+            {
+                SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+                        " != %" PRIu32 " from stream", ssn,
+                        TCP_GET_SEQ(p), ssn->client.next_seq);
+                StreamTcpSetEvent(p, STREAM_FIN1_FIN_WRONG_SEQ);
+                return -1;
+            }
+
+            if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) {
+                SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+                StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK);
+                return -1;
+            }
+
+            StreamTcpPacketSetState(p, ssn, TCP_CLOSING);
+            ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED;
+            SCLogDebug("ssn %p: state changed to TCP_CLOSING", ssn);
+
+            ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
+
+            StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
+
+            if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+                StreamTcpHandleTimestamp(ssn, p);
+            }
+
+            /* Update the next_seq, in case if we have missed the client
+               packet and server has already received and acked it */
+            if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
+                ssn->server.next_seq = TCP_GET_ACK(p);
+
+            if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) {
+                ssn->client.next_seq += p->payload_len;
+                SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "",
+                        ssn, ssn->client.next_seq);
+            }
+
+            StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+                    &ssn->client, p, pq);
+
+            SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+                    "%" PRIu32 "", ssn, ssn->client.next_seq,
+                    ssn->server.last_ack);
+        } else { /* implied to client */
+            SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ "
+                    "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
+                    TCP_GET_SEQ(p), TCP_GET_ACK(p));
+
+            if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) ||
+                    SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window)))
+            {
+                SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
+                        " != %" PRIu32 " from stream", ssn,
+                        TCP_GET_SEQ(p), ssn->server.next_seq);
+                StreamTcpSetEvent(p, STREAM_FIN1_FIN_WRONG_SEQ);
+                return -1;
+            }
+
+            if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) {
+                SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
+                StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK);
+                return -1;
+            }
+
+            StreamTcpPacketSetState(p, ssn, TCP_CLOSING);
+            ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED;
+            SCLogDebug("ssn %p: state changed to TCP_CLOSING", ssn);
+
+            ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
+
+            StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
+
+            if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
+                StreamTcpHandleTimestamp(ssn, p);
+            }
+
+            /* Update the next_seq, in case if we have missed the client
+               packet and server has already received and acked it */
+            if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
+                ssn->client.next_seq = TCP_GET_ACK(p);
+
+            if (SEQ_EQ(ssn->server.next_seq, TCP_GET_SEQ(p))) {
+                ssn->server.next_seq += p->payload_len;
+                SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "",
+                        ssn, ssn->server.next_seq);
+            }
+
+            StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
+                    &ssn->server, p, pq);
+
+            SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
+                    "%" PRIu32 "", ssn, ssn->server.next_seq,
+                    ssn->client.last_ack);
+        }
     } else if (p->tcph->th_flags & TH_SYN) {
         SCLogDebug("ssn (%p): SYN pkt on FinWait1", ssn);
         StreamTcpSetEvent(p, STREAM_SHUTDOWN_SYN_RESEND);