TCP: Zero Window Probe ACK detection for improper clients

This commit is contained in:
Eugène Adell 2023-06-11 17:11:26 +00:00 committed by AndersBroman
parent acbc327faf
commit d00468742f
2 changed files with 57 additions and 27 deletions

View file

@ -643,7 +643,8 @@ Set when the all of the following are true:
* The segment size is zero. * The segment size is zero.
* The window size is non-zero and not equal to the last-seen window size. * The window size is non-zero and not equal to the last-seen window size.
* The sequence number is equal to the next expected sequence number. * The sequence number is equal to the next expected sequence number.
* The acknowledgment number is equal to the last-seen acknowledgment number. * The acknowledgment number is equal to the last-seen acknowledgment number,
* or to the next expected sequence number when answering to a ZeroWindowProbe.
* None of SYN, FIN, or RST are set. * None of SYN, FIN, or RST are set.
// TCP_A_ZERO_WINDOW // TCP_A_ZERO_WINDOW

View file

@ -2274,13 +2274,23 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
&& window==0 && window==0
&& window==tcpd->fwd->window && window==tcpd->fwd->window
&& seq==tcpd->fwd->tcp_analyze_seq_info->nextseq && seq==tcpd->fwd->tcp_analyze_seq_info->nextseq
&& ack==tcpd->fwd->tcp_analyze_seq_info->lastack && (ack==tcpd->fwd->tcp_analyze_seq_info->lastack || EQ_SEQ(ack,tcpd->fwd->tcp_analyze_seq_info->lastack+1))
&& (tcpd->rev->lastsegmentflags&TCP_A_ZERO_WINDOW_PROBE) && (tcpd->rev->lastsegmentflags&TCP_A_ZERO_WINDOW_PROBE)
&& (flags&(TH_SYN|TH_FIN|TH_RST))==0 ) { && (flags&(TH_SYN|TH_FIN|TH_RST))==0 ) {
if(!tcpd->ta) { if(!tcpd->ta) {
tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd); tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
} }
tcpd->ta->flags|=TCP_A_ZERO_WINDOW_PROBE_ACK; tcpd->ta->flags|=TCP_A_ZERO_WINDOW_PROBE_ACK;
/* Some receivers consume that extra byte brought in the PROBE,
* but it was too early to know that during the WINDOW PROBE analysis.
* Do it now by moving the rev nextseq & maxseqtobeacked.
* See issue 10745.
*/
if(EQ_SEQ(ack,tcpd->fwd->tcp_analyze_seq_info->lastack+1)) {
tcpd->rev->tcp_analyze_seq_info->nextseq=ack;
tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=ack;
}
goto finished_fwd; goto finished_fwd;
} }
@ -2324,7 +2334,10 @@ finished_fwd:
/* ACKED LOST PACKET /* ACKED LOST PACKET
* If this segment acks beyond the 'max seq to be acked' in the other direction * If this segment acks beyond the 'max seq to be acked' in the other direction
* then that means we have missed packets going in the * then that means we have missed packets going in the
* other direction * other direction.
* It might also indicate we are resuming from a Zero Window,
* where a Probe is just followed by an ACK opening again the window.
* See issue 8404.
* *
* We only check this if we have actually seen some seq numbers * We only check this if we have actually seen some seq numbers
* in the other direction. * in the other direction.
@ -2336,33 +2349,46 @@ finished_fwd:
tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd); tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
} }
/* We ensure there is no matching packet waiting in the unacked list, /* resuming from a Zero Window Probe which re-opens the window,
* and take this opportunity to push the tail further than this single packet * mark it as a Window Update
*/ */
gboolean is_seq_in_unacked = FALSE; if(EQ_SEQ(ack,tcpd->fwd->tcp_analyze_seq_info->lastack+1)
guint32 maxseqtail = ack; && (seq==tcpd->fwd->tcp_analyze_seq_info->nextseq)
ual = tcpd->rev->tcp_analyze_seq_info->segments; && (tcpd->rev->lastsegmentflags&TCP_A_ZERO_WINDOW_PROBE) ) {
while(ual) { tcpd->rev->tcp_analyze_seq_info->nextseq=ack;
/* prevent false positives */ tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=ack;
if(GT_SEQ(ack,ual->seq) && LE_SEQ(ack,ual->nextseq)) { tcpd->ta->flags|=TCP_A_WINDOW_UPDATE;
is_seq_in_unacked = TRUE;
}
/* look for a possible tail pushing the maxseqtobeacked further */
if(maxseqtail==ual->seq) {
maxseqtail = ual->nextseq;
}
ual=ual->next;
}
/* update 'max seq to be acked' in the other direction so we don't get
* this indication again.
*/
if(is_seq_in_unacked) {
tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=(GT_SEQ(maxseqtail, ack)) ? ack : maxseqtail;
} }
/* real ACKED LOST PACKET */
else { else {
tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=maxseqtail; /* We ensure there is no matching packet waiting in the unacked list,
tcpd->ta->flags|=TCP_A_ACK_LOST_PACKET; * and take this opportunity to push the tail further than this single packet
*/
gboolean is_seq_in_unacked = FALSE;
guint32 maxseqtail = ack;
ual = tcpd->rev->tcp_analyze_seq_info->segments;
while(ual) {
/* prevent false positives */
if(GT_SEQ(ack,ual->seq) && LE_SEQ(ack,ual->nextseq)) {
is_seq_in_unacked = TRUE;
}
/* look for a possible tail pushing the maxseqtobeacked further */
if(maxseqtail==ual->seq) {
maxseqtail = ual->nextseq;
}
ual=ual->next;
}
/* update 'max seq to be acked' in the other direction so we don't get
* this indication again.
*/
if(is_seq_in_unacked) {
tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=(GT_SEQ(maxseqtail, ack)) ? ack : maxseqtail;
}
else {
tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=maxseqtail;
tcpd->ta->flags|=TCP_A_ACK_LOST_PACKET;
}
} }
} }
@ -2626,6 +2652,9 @@ finished_checking_retransmission_type:
* If this ever happens, this boundary value can "jump" further in order to * If this ever happens, this boundary value can "jump" further in order to
* avoid duplicating multiple messages for the very same lost packet. See later * avoid duplicating multiple messages for the very same lost packet. See later
* how ACKED LOST PACKET are handled. * how ACKED LOST PACKET are handled.
* Zero Window Probes are logically left out at this moment, but if their data
* really were to be ack'ed, then it will be done later when analyzing their
* Probe ACK (be it a real Probe ACK, or an ordinary ACK moving the RCV Window).
*/ */
if(EQ_SEQ(seq, tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked) || !tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked) { if(EQ_SEQ(seq, tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked) || !tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked) {
if( !tcpd->ta || !(tcpd->ta->flags&TCP_A_ZERO_WINDOW_PROBE) ) { if( !tcpd->ta || !(tcpd->ta->flags&TCP_A_ZERO_WINDOW_PROBE) ) {