zeek/src/Reassem.cc
Jon Siwek f4101b5265 Improve handling of IPv6 routing type 0 extension headers.
- flow_weird event with name argument value of "routing0_hdr" is raised
  for packets containing an IPv6 routing type 0 header because this
  type of header is now deprecated according to RFC 5095.

- packets with a routing type 0 header and non-zero segments left
  now use the last address in that header in order to associate
  with a connection/flow and for calculating TCP/UDP checksums.

- added a set of IPv4/IPv6 TCP/UDP checksum unit tests
2012-03-27 16:05:45 -05:00

312 lines
6.5 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#include <algorithm>
#include "config.h"
#include "Reassem.h"
#include "Serializer.h"
const bool DEBUG_reassem = false;
#ifdef DEBUG
int reassem_seen_bytes = 0;
int reassem_copied_bytes = 0;
#endif
DataBlock::DataBlock(const u_char* data, int size, int arg_seq,
DataBlock* arg_prev, DataBlock* arg_next)
{
seq = arg_seq;
upper = seq + size;
block = new u_char[size];
if ( ! block )
reporter->InternalError("out of memory");
memcpy((void*) block, (const void*) data, size);
#ifdef DEBUG
reassem_copied_bytes += size;
#endif
prev = arg_prev;
next = arg_next;
if ( prev )
prev->next = this;
if ( next )
next->prev = this;
Reassembler::total_size += pad_size(size) + padded_sizeof(DataBlock);
}
unsigned int Reassembler::total_size = 0;
Reassembler::Reassembler(int init_seq, ReassemblerType arg_type)
{
blocks = last_block = 0;
trim_seq = last_reassem_seq = init_seq;
}
Reassembler::~Reassembler()
{
ClearBlocks();
}
void Reassembler::NewBlock(double t, int seq, int len, const u_char* data)
{
if ( len == 0 )
return;
#ifdef DEBUG
reassem_seen_bytes += len;
#endif
int upper_seq = seq + len;
if ( seq_delta(upper_seq, trim_seq) <= 0 )
// Old data, don't do any work for it.
return;
if ( seq_delta(seq, trim_seq) < 0 )
{ // Partially old data, just keep the good stuff.
int amount_old = seq_delta(trim_seq, seq);
data += amount_old;
seq += amount_old;
len -= amount_old;
}
DataBlock* start_block;
if ( ! blocks )
blocks = last_block = start_block =
new DataBlock(data, len, seq, 0, 0);
else
start_block = AddAndCheck(blocks, seq, upper_seq, data);
BlockInserted(start_block);
}
int Reassembler::TrimToSeq(int seq)
{
int num_missing = 0;
// Do this accounting before looking for Undelivered data,
// since that will alter last_reassem_seq.
if ( blocks )
{
if ( seq_delta(blocks->seq, last_reassem_seq) > 0 )
// An initial hole.
num_missing += seq_delta(blocks->seq, last_reassem_seq);
}
else if ( seq_delta(seq, last_reassem_seq) > 0 )
{ // Trimming data we never delivered.
if ( ! blocks )
// We won't have any accounting based on blocks
// for this hole.
num_missing += seq_delta(seq, last_reassem_seq);
}
if ( seq_delta(seq, last_reassem_seq) > 0 )
{
// We're trimming data we never delivered.
Undelivered(seq);
}
while ( blocks && seq_delta(blocks->upper, seq) <= 0 )
{
DataBlock* b = blocks->next;
if ( b && seq_delta(b->seq, seq) <= 0 )
{
if ( blocks->upper != b->seq )
num_missing += seq_delta(b->seq, blocks->upper);
}
else
{
// No more blocks - did this one make it to seq?
// Second half of test is for acks of FINs, which
// don't get entered into the sequence space.
if ( blocks->upper != seq && blocks->upper != seq - 1 )
num_missing += seq_delta(seq, blocks->upper);
}
delete blocks;
blocks = b;
}
if ( blocks )
{
blocks->prev = 0;
// If we skipped over some undeliverable data, then
// it's possible that this block is now deliverable.
// Give it a try.
if ( blocks->seq == last_reassem_seq )
BlockInserted(blocks);
}
else
last_block = 0;
if ( seq_delta(seq, trim_seq) > 0 )
// seq is further ahead in the sequence space.
trim_seq = seq;
return num_missing;
}
void Reassembler::ClearBlocks()
{
while ( blocks )
{
DataBlock* b = blocks->next;
delete blocks;
blocks = b;
}
last_block = 0;
}
int Reassembler::TotalSize() const
{
int size = 0;
for ( DataBlock* b = blocks; b; b = b->next )
size += b->Size();
return size;
}
void Reassembler::Describe(ODesc* d) const
{
d->Add("reassembler");
}
void Reassembler::Undelivered(int up_to_seq)
{
// TrimToSeq() expects this.
last_reassem_seq = up_to_seq;
}
DataBlock* Reassembler::AddAndCheck(DataBlock* b, int seq, int upper,
const u_char* data)
{
if ( DEBUG_reassem )
{
DEBUG_MSG("%.6f Reassembler::AddAndCheck seq=%d, upper=%d\n",
network_time, seq, upper);
}
// Special check for the common case of appending to the end.
if ( last_block && seq == last_block->upper )
{
last_block = new DataBlock(data, upper - seq, seq,
last_block, 0);
return last_block;
}
// Find the first block that doesn't come completely before the
// new data.
while ( b->next && seq_delta(b->upper, seq) <= 0 )
b = b->next;
if ( seq_delta(b->upper, seq) <= 0 )
{
// b is the last block, and it comes completely before
// the new block.
last_block = new DataBlock(data, upper - seq, seq, b, 0);
return last_block;
}
DataBlock* new_b = 0;
if ( seq_delta(upper, b->seq) <= 0 )
{
// The new block comes completely before b.
new_b = new DataBlock(data, seq_delta(upper, seq), seq,
b->prev, b);
if ( b == blocks )
blocks = new_b;
return new_b;
}
// The blocks overlap, complain.
if ( seq_delta(seq, b->seq) < 0 )
{
// The new block has a prefix that comes before b.
int prefix_len = seq_delta(b->seq, seq);
new_b = new DataBlock(data, prefix_len, seq, b->prev, b);
if ( b == blocks )
blocks = new_b;
data += prefix_len;
seq += prefix_len;
}
else
new_b = b;
int overlap_start = seq;
int overlap_offset = seq_delta(overlap_start, b->seq);
int new_b_len = seq_delta(upper, seq);
int b_len = seq_delta(b->upper, overlap_start);
int overlap_len = min(new_b_len, b_len);
Overlap(&b->block[overlap_offset], data, overlap_len);
if ( overlap_len < new_b_len )
{
// Recurse to resolve remainder of the new data.
data += overlap_len;
seq += overlap_len;
if ( new_b == b )
new_b = AddAndCheck(b, seq, upper, data);
else
(void) AddAndCheck(b, seq, upper, data);
}
if ( new_b->prev == last_block )
last_block = new_b;
return new_b;
}
bool Reassembler::Serialize(SerialInfo* info) const
{
return SerialObj::Serialize(info);
}
Reassembler* Reassembler::Unserialize(UnserialInfo* info)
{
return (Reassembler*) SerialObj::Unserialize(info, SER_REASSEMBLER);
}
bool Reassembler::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_REASSEMBLER, BroObj);
// I'm not sure if it makes sense to actually save the buffered data.
// For now, we just remember the seq numbers so that we don't get
// complaints about missing content.
return SERIALIZE(trim_seq) && SERIALIZE(int(0));
}
bool Reassembler::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroObj);
blocks = last_block = 0;
int dummy; // For backwards compatibility.
if ( ! UNSERIALIZE(&trim_seq) || ! UNSERIALIZE(&dummy) )
return false;
last_reassem_seq = trim_seq;
return true;
}