diff --git a/src/stream-tcp.c b/src/stream-tcp.c index a926832fbf..74b07fa63c 100644 --- a/src/stream-tcp.c +++ b/src/stream-tcp.c @@ -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);