]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
stream: detect keep-alive and keep-alive ACK
authorVictor Julien <victor@inliniac.net>
Fri, 19 Apr 2013 16:51:23 +0000 (18:51 +0200)
committerVictor Julien <victor@inliniac.net>
Wed, 26 Jun 2013 13:01:25 +0000 (15:01 +0200)
src/stream-tcp-private.h
src/stream-tcp.c

index f26930784e009b3cb9729d7dea907f14ba8f6363..2933e3f700596526cea8c1c4d92ddb5ee0c2a40a 100644 (file)
@@ -150,9 +150,8 @@ enum
 #define STREAMTCP_STREAM_FLAG_GAP               0x01
 /** Flag to avoid stream reassembly/app layer inspection for the stream */
 #define STREAMTCP_STREAM_FLAG_NOREASSEMBLY      0x02
-
-/** vacancy at 0x04 */
-
+/** we received a keep alive */
+#define STREAMTCP_STREAM_FLAG_KEEPALIVE         0x04
 /** Stream has reached it's reassembly depth, all further packets are ignored */
 #define STREAMTCP_STREAM_FLAG_DEPTH_REACHED     0x08
 /** Stream has sent a FIN/RST */
index c9f25b985223da1163e757a36d8d0edc6e062b28..7ea28b98e5f07e2a7ec6b1810bd1cf0dba999fd2 100644 (file)
@@ -3970,6 +3970,165 @@ static int StreamTcpPacketStateTimeWait(ThreadVars *tv, Packet *p,
     return 0;
 }
 
+/**
+ *  \retval 1 packet is a keep alive pkt
+ *  \retval 0 packet is not a keep alive pkt
+ */
+static int StreamTcpPacketIsKeepAlive(TcpSession *ssn, Packet *p) {
+    TcpStream *stream = NULL, *ostream = NULL;
+    uint32_t seq;
+    uint32_t ack;
+
+    if (p->flags & PKT_PSEUDO_STREAM_END)
+        return 0;
+
+    /*
+       rfc 1122:
+       An implementation SHOULD send a keep-alive segment with no
+       data; however, it MAY be configurable to send a keep-alive
+       segment containing one garbage octet, for compatibility with
+       erroneous TCP implementations.
+     */
+    if (p->payload_len > 1)
+        return 0;
+
+    if ((p->tcph->th_flags & (TH_SYN|TH_FIN|TH_RST)) != 0) {
+        return 0;
+    }
+
+    if (PKT_IS_TOSERVER(p)) {
+        stream = &ssn->client;
+        ostream = &ssn->server;
+    } else {
+        stream = &ssn->server;
+        ostream = &ssn->client;
+    }
+
+    seq = TCP_GET_SEQ(p);
+    ack = TCP_GET_ACK(p);
+
+    if (ack == ostream->last_ack && seq == (stream->next_seq - 1)) {
+        SCLogDebug("packet is TCP keep-alive: %"PRIu64, p->pcap_cnt);
+        stream->flags |= STREAMTCP_STREAM_FLAG_KEEPALIVE;
+        return 1;
+    }
+    SCLogDebug("seq %u (%u), ack %u (%u)", seq,  (stream->next_seq - 1), ack, ostream->last_ack);
+    return 0;
+}
+
+/**
+ *  \retval 1 packet is a keep alive ACK pkt
+ *  \retval 0 packet is not a keep alive ACK pkt
+ */
+static int StreamTcpPacketIsKeepAliveACK(TcpSession *ssn, Packet *p) {
+    TcpStream *stream = NULL, *ostream = NULL;
+    uint32_t seq;
+    uint32_t ack;
+    uint32_t pkt_win;
+
+    if (p->flags & PKT_PSEUDO_STREAM_END)
+        return 0;
+    /* should get a normal ACK to a Keep Alive */
+    if (p->payload_len > 0)
+        return 0;
+
+    if ((p->tcph->th_flags & (TH_SYN|TH_FIN|TH_RST)) != 0)
+        return 0;
+
+    if (TCP_GET_WINDOW(p) == 0)
+        return 0;
+
+    if (PKT_IS_TOSERVER(p)) {
+        stream = &ssn->client;
+        ostream = &ssn->server;
+    } else {
+        stream = &ssn->server;
+        ostream = &ssn->client;
+    }
+
+    seq = TCP_GET_SEQ(p);
+    ack = TCP_GET_ACK(p);
+
+    pkt_win = TCP_GET_WINDOW(p) << ostream->wscale;
+    if (pkt_win != ostream->window)
+        return 0;
+
+    if ((ostream->flags & STREAMTCP_STREAM_FLAG_KEEPALIVE) && ack == ostream->last_ack && seq == stream->next_seq) {
+        SCLogDebug("packet is TCP keep-aliveACK: %"PRIu64, p->pcap_cnt);
+        ostream->flags &= ~STREAMTCP_STREAM_FLAG_KEEPALIVE;
+        return 1;
+    }
+    SCLogDebug("seq %u (%u), ack %u (%u) FLAG_KEEPALIVE: %s", seq, stream->next_seq, ack, ostream->last_ack,
+            ostream->flags & STREAMTCP_STREAM_FLAG_KEEPALIVE ? "set" : "not set");
+    return 0;
+}
+
+static void StreamTcpClearKeepAliveFlag(TcpSession *ssn, Packet *p) {
+    TcpStream *stream = NULL;
+
+    if (p->flags & PKT_PSEUDO_STREAM_END)
+        return;
+
+    if (PKT_IS_TOSERVER(p)) {
+        stream = &ssn->client;
+    } else {
+        stream = &ssn->server;
+    }
+
+    if (stream->flags & STREAMTCP_STREAM_FLAG_KEEPALIVE) {
+        stream->flags &= ~STREAMTCP_STREAM_FLAG_KEEPALIVE;
+        SCLogDebug("FLAG_KEEPALIVE cleared");
+    }
+}
+
+/**
+ *  \retval 1 packet is a window update pkt
+ *  \retval 0 packet is not a window update pkt
+ */
+static int StreamTcpPacketIsWindowUpdate(TcpSession *ssn, Packet *p) {
+    TcpStream *stream = NULL, *ostream = NULL;
+    uint32_t seq;
+    uint32_t ack;
+    uint32_t pkt_win;
+
+    if (p->flags & PKT_PSEUDO_STREAM_END)
+        return 0;
+
+    if (ssn->state < TCP_ESTABLISHED)
+        return 0;
+
+    if (p->payload_len > 0)
+        return 0;
+
+    if ((p->tcph->th_flags & (TH_SYN|TH_FIN|TH_RST)) != 0)
+        return 0;
+
+    if (TCP_GET_WINDOW(p) == 0)
+        return 0;
+
+    if (PKT_IS_TOSERVER(p)) {
+        stream = &ssn->client;
+        ostream = &ssn->server;
+    } else {
+        stream = &ssn->server;
+        ostream = &ssn->client;
+    }
+
+    seq = TCP_GET_SEQ(p);
+    ack = TCP_GET_ACK(p);
+
+    pkt_win = TCP_GET_WINDOW(p) << ostream->wscale;
+    if (pkt_win == ostream->window)
+        return 0;
+
+    if (ack == ostream->last_ack && seq == stream->next_seq) {
+        SCLogDebug("packet is TCP window update: %"PRIu64, p->pcap_cnt);
+        return 1;
+    }
+    SCLogDebug("seq %u (%u), ack %u (%u)", seq, stream->next_seq, ack, ostream->last_ack);
+    return 0;
+}
+
 /* flow is and stays locked */
 static int StreamTcpPacket (ThreadVars *tv, Packet *p, StreamTcpThread *stt,
                             PacketQueue *pq)
@@ -4025,6 +4184,16 @@ static int StreamTcpPacket (ThreadVars *tv, Packet *p, StreamTcpThread *stt,
         if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM_SYNACK)
             StreamTcpPacketSwitchDir(ssn, p);
 
+        StreamTcpPacketIsWindowUpdate(ssn, p);
+        if (StreamTcpPacketIsKeepAlive(ssn, p) == 1) {
+            goto skip;
+        }
+        if (StreamTcpPacketIsKeepAliveACK(ssn, p) == 1) {
+            StreamTcpClearKeepAliveFlag(ssn, p);
+            goto skip;
+        }
+        StreamTcpClearKeepAliveFlag(ssn, p);
+
         switch (ssn->state) {
             case TCP_SYN_SENT:
                 if(StreamTcpPacketStateSynSent(tv, p, stt, ssn, &stt->pseudo_queue)) {
@@ -4121,6 +4290,7 @@ static int StreamTcpPacket (ThreadVars *tv, Packet *p, StreamTcpThread *stt,
                 SCLogDebug("packet received on default state");
                 break;
         }
+    skip:
 
         if (ssn->state >= TCP_ESTABLISHED) {
             p->flags |= PKT_STREAM_EST;