From d9008614a22697594958be073f2a94da9e8d85f2 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Mon, 6 Mar 2023 16:06:25 +0100 Subject: [PATCH] stream: handle zero window probe acks These can be skipped for the most part. --- src/output-eve-stream.c | 2 ++ src/stream-tcp-private.h | 1 + src/stream-tcp.c | 47 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/src/output-eve-stream.c b/src/output-eve-stream.c index e0d98fb8ea..f986cdff4d 100644 --- a/src/output-eve-stream.c +++ b/src/output-eve-stream.c @@ -357,6 +357,8 @@ static int EveStreamLogger(ThreadVars *tv, void *thread_data, const Packet *p) jb_append_string(js, "tcp_port_reuse"); if (p->tcpvars.stream_pkt_flags & STREAM_PKT_FLAG_TCP_ZERO_WIN_PROBE) jb_append_string(js, "zero_window_probe"); + if (p->tcpvars.stream_pkt_flags & STREAM_PKT_FLAG_TCP_ZERO_WIN_PROBE_ACK) + jb_append_string(js, "zero_window_probe_ack"); jb_close(js); } jb_close(js); diff --git a/src/stream-tcp-private.h b/src/stream-tcp-private.h index b827535eff..a06e9cf353 100644 --- a/src/stream-tcp-private.h +++ b/src/stream-tcp-private.h @@ -318,6 +318,7 @@ typedef struct TcpSession_ { #define STREAM_PKT_FLAG_ACK_UNSEEN_DATA BIT_U16(9) #define STREAM_PKT_FLAG_TCP_PORT_REUSE BIT_U16(10) #define STREAM_PKT_FLAG_TCP_ZERO_WIN_PROBE BIT_U16(11) +#define STREAM_PKT_FLAG_TCP_ZERO_WIN_PROBE_ACK BIT_U16(12) #define STREAM_PKT_FLAG_SET(p, f) (p)->tcpvars.stream_pkt_flags |= (f) diff --git a/src/stream-tcp.c b/src/stream-tcp.c index 904769b4dd..2bc40137bb 100644 --- a/src/stream-tcp.c +++ b/src/stream-tcp.c @@ -2851,6 +2851,42 @@ static inline uint32_t StreamTcpResetGetMaxAck(TcpStream *stream, uint32_t seq) SCReturnUInt(ack); } +static bool StreamTcpPacketIsZeroWindowProbeAck(const TcpSession *ssn, const Packet *p) +{ + if (ssn->state < TCP_ESTABLISHED) + return false; + if (p->payload_len != 0) + return false; + if ((p->tcph->th_flags & (TH_ACK | TH_SYN | TH_FIN | TH_RST)) != TH_ACK) + return false; + + const TcpStream *snd, *rcv; + if (PKT_IS_TOCLIENT(p)) { + snd = &ssn->server; + rcv = &ssn->client; + if (!(ssn->flags & STREAMTCP_FLAG_ZWP_TS)) + return false; + } else { + snd = &ssn->client; + rcv = &ssn->server; + if (!(ssn->flags & STREAMTCP_FLAG_ZWP_TC)) + return false; + } + + const uint32_t pkt_win = TCP_GET_WINDOW(p) << snd->wscale; + if (pkt_win != 0) + return false; + if (pkt_win != rcv->window) + return false; + + if (TCP_GET_SEQ(p) != snd->next_seq) + return false; + if (TCP_GET_ACK(p) != rcv->last_ack) + return false; + SCLogDebug("ssn %p: packet %" PRIu64 " is a Zero Window Probe ACK", ssn, p->pcap_cnt); + return true; +} + /** \internal * \brief check if an ACK packet is a dup-ACK */ @@ -5364,6 +5400,17 @@ int StreamTcpPacket (ThreadVars *tv, Packet *p, StreamTcpThread *stt, } StreamTcpClearKeepAliveFlag(ssn, p); + const bool is_zwp_ack = StreamTcpPacketIsZeroWindowProbeAck(ssn, p); + if (PKT_IS_TOCLIENT(p)) { + ssn->flags &= ~STREAMTCP_FLAG_ZWP_TS; + } else { + ssn->flags &= ~STREAMTCP_FLAG_ZWP_TC; + } + if (is_zwp_ack) { + STREAM_PKT_FLAG_SET(p, STREAM_PKT_FLAG_TCP_ZERO_WIN_PROBE_ACK); + goto skip; + } + if (StreamTcpPacketIsDupAck(ssn, p) == true) { STREAM_PKT_FLAG_SET(p, STREAM_PKT_FLAG_DUP_ACK); // TODO see if we can skip work on these