mirror of
https://github.com/zeek/zeek.git
synced 2025-10-03 15:18:20 +00:00
Fix issue w/ TCP reassembler not delivering some segments.
For example, if we have a connection between TCP "A" and TCP "B" and "A" sends segments "1" and "2", but we don't see the first and then the next acknowledgement from "B" is for everything up to, and including, "2", the gap would be reported to include both segments instead of just the first and then delivering the second. Put generally: any segments that weren't yet delivered because they're waiting for an earlier gap to be filled would be dropped when an ACK comes in that includes the gap as well as those pending segments. (If a distinct ACK was seen for just the gap, that situation would have worked). Addresses BIT-1246.
This commit is contained in:
parent
f97f58e9db
commit
f1cef9d2a9
7 changed files with 96 additions and 39 deletions
|
@ -117,6 +117,45 @@ void TCP_Reassembler::SetContentsFile(BroFile* f)
|
|||
record_contents_file = f;
|
||||
}
|
||||
|
||||
static inline bool established(const TCP_Endpoint* a, const TCP_Endpoint* b)
|
||||
{
|
||||
return a->state == TCP_ENDPOINT_ESTABLISHED &&
|
||||
b->state == TCP_ENDPOINT_ESTABLISHED;
|
||||
}
|
||||
|
||||
static inline bool report_gap(const TCP_Endpoint* a, const TCP_Endpoint* b)
|
||||
{
|
||||
return content_gap &&
|
||||
( BifConst::report_gaps_for_partial || established(a, b) );
|
||||
}
|
||||
|
||||
void TCP_Reassembler::Gap(uint64 seq, uint64 len)
|
||||
{
|
||||
// Only report on content gaps for connections that
|
||||
// are in a cleanly established state. In other
|
||||
// states, these can arise falsely due to things
|
||||
// like sequence number mismatches in RSTs, or
|
||||
// unseen previous packets in partial connections.
|
||||
// The one opportunity we lose here is on clean FIN
|
||||
// handshakes, but Oh Well.
|
||||
|
||||
if ( report_gap(endp, endp->peer) )
|
||||
{
|
||||
val_list* vl = new val_list;
|
||||
vl->append(dst_analyzer->BuildConnVal());
|
||||
vl->append(new Val(IsOrig(), TYPE_BOOL));
|
||||
vl->append(new Val(seq, TYPE_COUNT));
|
||||
vl->append(new Val(len, TYPE_COUNT));
|
||||
dst_analyzer->ConnectionEvent(content_gap, vl);
|
||||
}
|
||||
|
||||
if ( type == Direct )
|
||||
dst_analyzer->NextUndelivered(seq, len, IsOrig());
|
||||
else
|
||||
dst_analyzer->ForwardUndelivered(seq, len, IsOrig());
|
||||
|
||||
had_gap = true;
|
||||
}
|
||||
|
||||
void TCP_Reassembler::Undelivered(uint64 up_to_seq)
|
||||
{
|
||||
|
@ -189,48 +228,33 @@ void TCP_Reassembler::Undelivered(uint64 up_to_seq)
|
|||
|
||||
if ( ! skip_deliveries )
|
||||
{
|
||||
// This can happen because we're processing a trace
|
||||
// that's been filtered. For example, if it's just
|
||||
// SYN/FIN data, then there can be data in the FIN
|
||||
// packet, but it's undelievered because it's out of
|
||||
// sequence.
|
||||
|
||||
uint64 seq = last_reassem_seq;
|
||||
uint64 len = up_to_seq - last_reassem_seq;
|
||||
|
||||
// Only report on content gaps for connections that
|
||||
// are in a cleanly established state. In other
|
||||
// states, these can arise falsely due to things
|
||||
// like sequence number mismatches in RSTs, or
|
||||
// unseen previous packets in partial connections.
|
||||
// The one opportunity we lose here is on clean FIN
|
||||
// handshakes, but Oh Well.
|
||||
|
||||
if ( content_gap &&
|
||||
(BifConst::report_gaps_for_partial ||
|
||||
(endpoint->state == TCP_ENDPOINT_ESTABLISHED &&
|
||||
peer->state == TCP_ENDPOINT_ESTABLISHED ) ) )
|
||||
DataBlock* b = blocks;
|
||||
// If we have blocks that begin below up_to_seq, deliver them.
|
||||
while ( b )
|
||||
{
|
||||
val_list* vl = new val_list;
|
||||
vl->append(dst_analyzer->BuildConnVal());
|
||||
vl->append(new Val(IsOrig(), TYPE_BOOL));
|
||||
vl->append(new Val(seq, TYPE_COUNT));
|
||||
vl->append(new Val(len, TYPE_COUNT));
|
||||
if ( b->seq < last_reassem_seq )
|
||||
{
|
||||
// Already delivered this block.
|
||||
b = b->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
dst_analyzer->ConnectionEvent(content_gap, vl);
|
||||
if ( b->seq >= up_to_seq )
|
||||
// Block is beyond what we need to process at this point.
|
||||
break;
|
||||
|
||||
uint64 gap_at_seq = last_reassem_seq;
|
||||
uint64 gap_len = b->seq - last_reassem_seq;
|
||||
|
||||
Gap(gap_at_seq, gap_len);
|
||||
last_reassem_seq += gap_len;
|
||||
BlockInserted(b);
|
||||
b = b->next;
|
||||
}
|
||||
|
||||
if ( type == Direct )
|
||||
dst_analyzer->NextUndelivered(last_reassem_seq,
|
||||
len, IsOrig());
|
||||
else
|
||||
{
|
||||
dst_analyzer->ForwardUndelivered(last_reassem_seq,
|
||||
len, IsOrig());
|
||||
}
|
||||
if ( up_to_seq > last_reassem_seq )
|
||||
Gap(last_reassem_seq, up_to_seq - last_reassem_seq);
|
||||
}
|
||||
|
||||
had_gap = true;
|
||||
}
|
||||
|
||||
// We should record and match undelivered even if we are skipping
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue