From: Victor Julien Date: Wed, 17 Oct 2012 07:25:52 +0000 (+0200) Subject: stream: change how retransmissions are handled and detected. X-Git-Tag: suricata-1.4beta3~77 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e1321f9ae6dff2222ea7d6630749d9a98354d4bc;p=thirdparty%2Fsuricata.git stream: change how retransmissions are handled and detected. --- diff --git a/rules/stream-events.rules b/rules/stream-events.rules index af129f1014..f97eb1a7d0 100644 --- a/rules/stream-events.rules +++ b/rules/stream-events.rules @@ -20,12 +20,9 @@ alert tcp any any -> any any (msg:"SURICATA STREAM 4way handshake invalid ack"; alert tcp any any -> any any (msg:"SURICATA STREAM CLOSEWAIT ACK out of window"; stream-event:closewait_ack_out_of_window; sid:2210015; rev:1;) alert tcp any any -> any any (msg:"SURICATA STREAM CLOSEWAIT FIN out of window"; stream-event:closewait_fin_out_of_window; sid:2210016; rev:1;) alert tcp any any -> any any (msg:"SURICATA STREAM CLOSEWAIT invalid ACK"; stream-event:closewait_invalid_ack; sid:2210017; rev:1;) -#alert tcp any any -> any any (msg:"SURICATA STREAM CLOSEWAIT retransmission packet before last ack"; stream-event:closewait_pkt_before_last_ack; sid:2210052; rev:2;) alert tcp any any -> any any (msg:"SURICATA STREAM CLOSING ACK wrong seq"; stream-event:closing_ack_wrong_seq; sid:2210018; rev:1;) alert tcp any any -> any any (msg:"SURICATA STREAM CLOSING invalid ACK"; stream-event:closing_invalid_ack; sid:2210019; rev:1;) alert tcp any any -> any any (msg:"SURICATA STREAM ESTABLISHED packet out of window"; stream-event:est_packet_out_of_window; sid:2210020; rev:1;) -# "regular" retransmissions -#alert tcp any any -> any any (msg:"SURICATA STREAM ESTABLISHED retransmission packet before last ack"; stream-event:est_pkt_before_last_ack; sid:2210021; rev:2;) alert tcp any any -> any any (msg:"SURICATA STREAM ESTABLISHED SYNACK resend"; stream-event:est_synack_resend; sid:2210022; rev:1;) alert tcp any any -> any any (msg:"SURICATA STREAM ESTABLISHED SYNACK resend with different ACK"; stream-event:est_synack_resend_with_different_ack; sid:2210023; rev:1;) alert tcp any any -> any any (msg:"SURICATA STREAM ESTABLISHED SYNACK resend with different seq"; stream-event:est_synack_resend_with_diff_seq; sid:2210024; rev:1;) @@ -58,9 +55,27 @@ alert tcp any any -> any any (msg:"SURICATA STREAM Packet with invalid ack"; str alert tcp any any -> any any (msg:"SURICATA STREAM SHUTDOWN RST invalid ack"; stream-event:rst_invalid_ack; sid:2210046; rev:1;) # SYN (re)send during shutdown (closing, closewait, finwait1, finwait2, lastack, timewait states) #alert tcp any any -> any any (msg:"SURICATA STREAM SYN resend"; stream-event:shutdown_syn_resend; sid:2210049; rev:1;) -#alert tcp any any -> any any (msg:"SURICATA STREAM reassembly segment before base seq"; stream-event:reassembly_segment_before_base_seq; sid:2210047; rev:1;) # Sequence gap: missing data in the reassembly engine. Usually due to packet loss. Will be very noisy on a overloaded link / sensor. #alert tcp any any -> any any (msg:"SURICATA STREAM reassembly sequence GAP -- missing packet(s)"; stream-event:reassembly_seq_gap; sid:2210048; rev:1;) alert tcp any any -> any any (msg:"SURICATA STREAM reassembly overlap with different data"; stream-event:reassembly_overlap_different_data; sid:2210050; rev:1;) -# next sid 2210053 + +# retransmission detection +# +# The rules below match on retransmissions detected in various stages of the +# stream engine. They are all "noalert" rules that increment the counter +# tcp.retransmission.count. The last rule sid:2210054 matches if the counter +# reaches 10. Increase this number if the rule is too noisy. +# +# "regular" retransmissions, only count +alert tcp any any -> any any (msg:"SURICATA STREAM ESTABLISHED retransmission packet before last ack"; stream-event:est_pkt_before_last_ack; flowint:tcp.retransmission.count,+,1; noalert; classtype:protocol-command-decode; sid:2210021; rev:3;) +# retransmission, only count +alert tcp any any -> any any (msg:"SURICATA STREAM CLOSEWAIT retransmission packet before last ack"; stream-event:closewait_pkt_before_last_ack; flowint:tcp.retransmission.count,+,1; noalert; classtype:protocol-command-decode; sid:2210052; rev:3;) +# retransmission of pkt before reassembly window, only count +alert tcp any any -> any any (msg:"SURICATA STREAM reassembly segment before base seq (retransmission)"; stream-event:reassembly_segment_before_base_seq; flowint:tcp.retransmission.count,+,1; noalert; classtype:protocol-command-decode; sid:2210047; rev:2;) +# count "general" retransmissions +alert tcp any any -> any any (msg:"SURICATA STREAM Packet is retransmission"; stream-event:pkt_retransmission; flowint:tcp.retransmission.count,+,1; noalert; classtype:protocol-command-decode; sid:2210053; rev:1;) +# rule to alert if a stream has excessive retransmissions +alert tcp any any -> any any (msg:"SURICATA STREAM excessive retransmissions"; flowbits:isnotset,tcp.retransmission.alerted; flowint:tcp.retransmission.count,>=,10; flowbits:set,tcp.retransmission.alerted; classtype:protocol-command-decode; sid:2210054; rev:1;) + +# next sid 2210055 diff --git a/src/decode-events.h b/src/decode-events.h index 697279fdd8..f03b77ffd5 100644 --- a/src/decode-events.h +++ b/src/decode-events.h @@ -179,6 +179,7 @@ enum { STREAM_PKT_INVALID_ACK, STREAM_PKT_BROKEN_ACK, STREAM_RST_INVALID_ACK, + STREAM_PKT_RETRANSMISSION, STREAM_REASSEMBLY_SEGMENT_BEFORE_BASE_SEQ, STREAM_REASSEMBLY_NO_SEGMENT, diff --git a/src/detect-engine-event.h b/src/detect-engine-event.h index 5bde8df714..5a4a56a76e 100644 --- a/src/detect-engine-event.h +++ b/src/detect-engine-event.h @@ -170,6 +170,7 @@ struct DetectEngineEvents_ { { "stream.pkt_broken_ack", STREAM_PKT_BROKEN_ACK, }, { "stream.rst_invalid_ack", STREAM_RST_INVALID_ACK, }, { "stream.shutdown_syn_resend", STREAM_SHUTDOWN_SYN_RESEND, }, + { "stream.pkt_retransmission", STREAM_PKT_RETRANSMISSION, }, { "stream.reassembly_segment_before_base_seq", STREAM_REASSEMBLY_SEGMENT_BEFORE_BASE_SEQ, }, { "stream.reassembly_no_segment", STREAM_REASSEMBLY_NO_SEGMENT, }, { "stream.reassembly_seq_gap", STREAM_REASSEMBLY_SEQ_GAP, }, diff --git a/src/stream-tcp.c b/src/stream-tcp.c index 1567c01504..f25379d0da 100644 --- a/src/stream-tcp.c +++ b/src/stream-tcp.c @@ -636,6 +636,27 @@ void StreamTcpSetOSPolicy(TcpStream *stream, Packet *p) } \ } +static int StreamTcpPacketIsRetransmission(TcpStream *stream, Packet *p) { + if (p->payload_len == 0) + SCReturnInt(0); + + /* retransmission of already ack'd data */ + if (SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), stream->last_ack)) { + StreamTcpSetEvent(p, STREAM_PKT_RETRANSMISSION); + SCReturnInt(1); + } + + /* retransmission of in flight data */ + if (SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), stream->next_seq)) { + StreamTcpSetEvent(p, STREAM_PKT_RETRANSMISSION); + SCReturnInt(2); + } + + SCLogDebug("seq %u payload_len %u => %u, last_ack %u", TCP_GET_SEQ(p), + p->payload_len, (TCP_GET_SEQ(p) + p->payload_len), stream->last_ack); + SCReturnInt(0); +} + /** * \internal * \brief Function to handle the TCP_CLOSED or NONE state. The function handles @@ -2258,8 +2279,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *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)); + int retransmission = 0; - if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) || + if (StreamTcpPacketIsRetransmission(&ssn->client, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + retransmission = 1; + + } else 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 "" @@ -2275,11 +2301,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p, return -1; } - StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); + if (!retransmission) { + StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); + ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + } StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); @@ -2308,8 +2336,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p, SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p)); + int retransmission = 0; - if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || + if (StreamTcpPacketIsRetransmission(&ssn->server, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + retransmission = 1; + + } else 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 "" @@ -2325,11 +2358,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p, return -1; } - StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); + if (!retransmission) { + StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); + ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; + SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + } StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); @@ -2366,8 +2401,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *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)); + int retransmission = 0; - if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) || + if (StreamTcpPacketIsRetransmission(&ssn->client, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + retransmission = 1; + + } else 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 "" @@ -2383,11 +2423,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p, return -1; } - StreamTcpPacketSetState(p, ssn, TCP_CLOSING); - ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: state changed to TCP_CLOSING", ssn); + if (!retransmission) { + 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; + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + } StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); @@ -2417,7 +2459,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p, "%" 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) || + int retransmission = 0; + + if (StreamTcpPacketIsRetransmission(&ssn->server, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + retransmission = 1; + + } else 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 "" @@ -2433,11 +2481,13 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p, return -1; } - StreamTcpPacketSetState(p, ssn, TCP_CLOSING); - ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; - SCLogDebug("ssn %p: state changed to TCP_CLOSING", ssn); + if (!retransmission) { + 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; + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + } StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); @@ -2478,6 +2528,12 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *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)); + int retransmission = 0; + + if (StreamTcpPacketIsRetransmission(&ssn->client, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + retransmission = 1; + } if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) { SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); @@ -2485,28 +2541,30 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p, return -1; } - if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) || - (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || - ssn->flags & STREAMTCP_FLAG_ASYNC) - { - SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win " - "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win); + if (!retransmission) { + if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) || + (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || + ssn->flags & STREAMTCP_FLAG_ASYNC) + { + SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win " + "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win); + + if (TCP_GET_SEQ(p) == ssn->client.next_seq) { + StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2); + SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn); + } + } else { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->client.next_seq); - if (TCP_GET_SEQ(p) == ssn->client.next_seq) { - StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2); - SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn); + StreamTcpSetEvent(p, STREAM_FIN1_ACK_WRONG_SEQ); + return -1; } - } else { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->client.next_seq); - StreamTcpSetEvent(p, STREAM_FIN1_ACK_WRONG_SEQ); - return -1; + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; } - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; - StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) { @@ -2542,32 +2600,41 @@ static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p, "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p)); + int retransmission = 0; + + if (StreamTcpPacketIsRetransmission(&ssn->server, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + retransmission = 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; } - if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) || - (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || - ssn->flags & STREAMTCP_FLAG_ASYNC) - { - SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win " - "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win); + if (!retransmission) { + if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) || + (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || + ssn->flags & STREAMTCP_FLAG_ASYNC) + { + SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win " + "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win); - if (TCP_GET_SEQ(p) == ssn->server.next_seq) { - StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2); - SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn); + if (TCP_GET_SEQ(p) == ssn->server.next_seq) { + StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2); + SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn); + } + } else { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->server.next_seq); + StreamTcpSetEvent(p, STREAM_FIN1_ACK_WRONG_SEQ); + return -1; } - } else { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->server.next_seq); - StreamTcpSetEvent(p, STREAM_FIN1_ACK_WRONG_SEQ); - return -1; - } - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + } StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); @@ -2679,6 +2746,9 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p, SEQ_EQ(TCP_GET_ACK(p), ssn->server.last_ack)) { SCLogDebug("ssn %p: retransmission", ssn); retransmission = 1; + } else if (StreamTcpPacketIsRetransmission(&ssn->client, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + retransmission = 1; } else if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) || SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window))) @@ -2700,9 +2770,9 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p, StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); ssn->client.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); - } - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + } StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); @@ -2733,6 +2803,9 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p, SEQ_EQ(TCP_GET_ACK(p), ssn->client.last_ack)) { SCLogDebug("ssn %p: retransmission", ssn); retransmission = 1; + } else if (StreamTcpPacketIsRetransmission(&ssn->server, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + retransmission = 1; } else if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) @@ -2754,8 +2827,9 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p, StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT); ssn->server.flags |= STREAMTCP_STREAM_FLAG_CLOSE_INITIATED; SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn); + + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; } - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); @@ -2792,6 +2866,12 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *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)); + int retransmission = 0; + + if (StreamTcpPacketIsRetransmission(&ssn->client, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + retransmission = 1; + } if (StreamTcpValidateAck(ssn, &ssn->server, p) == -1) { SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); @@ -2799,22 +2879,24 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p, return -1; } - if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) || - (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || - ssn->flags & STREAMTCP_FLAG_ASYNC) - { - SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win " - "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win); + if (!retransmission) { + if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) || + (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || + ssn->flags & STREAMTCP_FLAG_ASYNC) + { + SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win " + "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win); - } else { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->client.next_seq); - StreamTcpSetEvent(p, STREAM_FIN2_ACK_WRONG_SEQ); - return -1; - } + } else { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->client.next_seq); + StreamTcpSetEvent(p, STREAM_FIN2_ACK_WRONG_SEQ); + return -1; + } - ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale; + } StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p)); @@ -2843,6 +2925,12 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p, SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ " "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p)); + int retransmission = 0; + + if (StreamTcpPacketIsRetransmission(&ssn->server, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + retransmission = 1; + } if (StreamTcpValidateAck(ssn, &ssn->client, p) == -1) { SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn); @@ -2850,21 +2938,23 @@ static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p, return -1; } - if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) || - (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || - ssn->flags & STREAMTCP_FLAG_ASYNC) - { - SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win " - "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win); - } else { - SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" - " != %" PRIu32 " from stream", ssn, - TCP_GET_SEQ(p), ssn->server.next_seq); - StreamTcpSetEvent(p, STREAM_FIN2_ACK_WRONG_SEQ); - return -1; - } + if (!retransmission) { + if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) || + (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) || + ssn->flags & STREAMTCP_FLAG_ASYNC) + { + SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win " + "%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win); + } else { + SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" + " != %" PRIu32 " from stream", ssn, + TCP_GET_SEQ(p), ssn->server.next_seq); + StreamTcpSetEvent(p, STREAM_FIN2_ACK_WRONG_SEQ); + return -1; + } - ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale; + } StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p)); @@ -2971,6 +3061,11 @@ static int StreamTcpPacketStateClosing(ThreadVars *tv, Packet *p, "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p)); + if (StreamTcpPacketIsRetransmission(&ssn->client, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + SCReturnInt(-1); + } + if (TCP_GET_SEQ(p) != ssn->client.next_seq) { SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" " != %" PRIu32 " from stream", ssn, @@ -3010,6 +3105,11 @@ static int StreamTcpPacketStateClosing(ThreadVars *tv, Packet *p, "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p)); + if (StreamTcpPacketIsRetransmission(&ssn->server, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + SCReturnInt(-1); + } + if (TCP_GET_SEQ(p) != ssn->server.next_seq) { SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" " != %" PRIu32 " from stream", ssn, @@ -3135,6 +3235,11 @@ static int StreamTcpPacketStateCloseWait(ThreadVars *tv, Packet *p, "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p)); + if (StreamTcpPacketIsRetransmission(&ssn->client, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + SCReturnInt(-1); + } + if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) || SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window))) { @@ -3178,6 +3283,11 @@ static int StreamTcpPacketStateCloseWait(ThreadVars *tv, Packet *p, "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p)); + if (StreamTcpPacketIsRetransmission(&ssn->server, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + SCReturnInt(-1); + } + if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) { @@ -3234,6 +3344,11 @@ static int StreamTcpPacketStateCloseWait(ThreadVars *tv, Packet *p, "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p)); + if (StreamTcpPacketIsRetransmission(&ssn->client, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + SCReturnInt(-1); + } + if (p->payload_len > 0 && (SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), ssn->client.last_ack))) { SCLogDebug("ssn %p: -> retransmission", ssn); StreamTcpSetEvent(p, STREAM_CLOSEWAIT_PKT_BEFORE_LAST_ACK); @@ -3280,6 +3395,11 @@ static int StreamTcpPacketStateCloseWait(ThreadVars *tv, Packet *p, "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p)); + if (StreamTcpPacketIsRetransmission(&ssn->server, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + SCReturnInt(-1); + } + if (p->payload_len > 0 && (SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), ssn->server.last_ack))) { SCLogDebug("ssn %p: -> retransmission", ssn); StreamTcpSetEvent(p, STREAM_CLOSEWAIT_PKT_BEFORE_LAST_ACK); @@ -3406,6 +3526,11 @@ static int StreamTcpPakcetStateLastAck(ThreadVars *tv, Packet *p, "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p)); + if (StreamTcpPacketIsRetransmission(&ssn->client, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + SCReturnInt(-1); + } + if (TCP_GET_SEQ(p) != ssn->client.next_seq && TCP_GET_SEQ(p) != ssn->client.next_seq + 1) { SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" " != %" PRIu32 " from stream", ssn, @@ -3526,6 +3651,11 @@ static int StreamTcpPacketStateTimeWait(ThreadVars *tv, Packet *p, "%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p)); + if (StreamTcpPacketIsRetransmission(&ssn->client, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + SCReturnInt(-1); + } + if (TCP_GET_SEQ(p) != ssn->client.next_seq && TCP_GET_SEQ(p) != ssn->client.next_seq+1) { SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" " != %" PRIu32 " from stream", ssn, @@ -3564,19 +3694,19 @@ static int StreamTcpPacketStateTimeWait(ThreadVars *tv, Packet *p, StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq); } else { - int retransmission = 0; - 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 (StreamTcpPacketIsRetransmission(&ssn->server, p)) { + SCLogDebug("ssn %p: packet is retransmission", ssn); + SCReturnInt(-1); + } + if (TCP_GET_SEQ(p) != ssn->server.next_seq && TCP_GET_SEQ(p) != ssn->server.next_seq+1) { if (p->payload_len > 0 && TCP_GET_SEQ(p) == ssn->server.last_ack) { SCLogDebug("ssn %p: -> retransmission", ssn); - retransmission = 1; - } else if (p->payload_len > 0 && SEQ_LEQ((TCP_GET_SEQ(p) + p->payload_len), ssn->server.last_ack)) { - SCLogDebug("ssn %p: -> retransmission", ssn); - retransmission = 1; + SCReturnInt(0); } else { SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 "" " != %" PRIu32 " from stream", ssn, @@ -3592,10 +3722,8 @@ static int StreamTcpPacketStateTimeWait(ThreadVars *tv, Packet *p, SCReturnInt(-1); } - if (!retransmission) { - StreamTcpPacketSetState(p, ssn, TCP_CLOSED); - SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn); - } + StreamTcpPacketSetState(p, ssn, TCP_CLOSED); + SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn); ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;