zeek/src/analyzer/protocol/udp/UDP.cc
Jon Siwek 2b3c2bd394 Fix reassembly of data w/ sizes beyond 32-bit capacities (BIT-348).
The main change is that reassembly code (e.g. for TCP) now uses
int64/uint64 (signedness is situational) data types in place of int
types in order to support delivering data to analyzers that pass 2GB
thresholds.  There's also changes in logic that accompany the change in
data types, e.g. to fix TCP sequence space arithmetic inconsistencies.

Another significant change is in the Analyzer API: the *Packet and
*Undelivered methods now use a uint64 in place of an int for the
relative sequence space offset parameter.
2014-04-09 13:03:24 -05:00

230 lines
5 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#include <algorithm>
#include "config.h"
#include "Net.h"
#include "NetVar.h"
#include "analyzer/protocol/udp/UDP.h"
#include "Reporter.h"
#include "Conn.h"
#include "events.bif.h"
using namespace analyzer::udp;
UDP_Analyzer::UDP_Analyzer(Connection* conn)
: TransportLayerAnalyzer("UDP", conn)
{
conn->EnableStatusUpdateTimer();
conn->SetInactivityTimeout(udp_inactivity_timeout);
request_len = reply_len = -1; // -1 means "haven't seen any activity"
}
UDP_Analyzer::~UDP_Analyzer()
{
// XXX: need to implement this!
// delete src_pkt_writer;
}
void UDP_Analyzer::Init()
{
}
void UDP_Analyzer::Done()
{
Analyzer::Done();
}
void UDP_Analyzer::DeliverPacket(int len, const u_char* data, bool is_orig,
uint64 seq, const IP_Hdr* ip, int caplen)
{
assert(ip);
Analyzer::DeliverPacket(len, data, is_orig, seq, ip, caplen);
const struct udphdr* up = (const struct udphdr*) data;
// Increment data before checksum check so that data will
// point to UDP payload even if checksum fails. Particularly,
// it allows event packet_contents to get to the data.
data += sizeof(struct udphdr);
// We need the min() here because Ethernet frame padding can lead to
// caplen > len.
if ( packet_contents )
PacketContents(data, min(len, caplen) - sizeof(struct udphdr));
int chksum = up->uh_sum;
if ( ! ignore_checksums && caplen >= len )
{
bool bad = false;
if ( ip->IP4_Hdr() )
{
if ( chksum && ! ValidateChecksum(ip, up, len) )
bad = true;
}
/* checksum is not optional for IPv6 */
else if ( ! ValidateChecksum(ip, up, len) )
bad = true;
if ( bad )
{
Weird("bad_UDP_checksum");
if ( is_orig )
Conn()->CheckHistory(HIST_ORIG_CORRUPT_PKT, 'C');
else
Conn()->CheckHistory(HIST_RESP_CORRUPT_PKT, 'c');
return;
}
}
int ulen = ntohs(up->uh_ulen);
if ( ulen != len )
Weird(fmt("UDP_datagram_length_mismatch(%d!=%d)", ulen, len));
len -= sizeof(struct udphdr);
ulen -= sizeof(struct udphdr);
caplen -= sizeof(struct udphdr);
Conn()->SetLastTime(current_timestamp);
if ( udp_contents )
{
PortVal port_val(ntohs(up->uh_dport), TRANSPORT_UDP);
Val* result = 0;
bool do_udp_contents = false;
if ( is_orig )
{
result = udp_content_delivery_ports_orig->Lookup(
&port_val);
if ( udp_content_deliver_all_orig ||
(result && result->AsBool()) )
do_udp_contents = true;
}
else
{
result = udp_content_delivery_ports_resp->Lookup(
&port_val);
if ( udp_content_deliver_all_resp ||
(result && result->AsBool()) )
do_udp_contents = true;
}
if ( do_udp_contents )
{
val_list* vl = new val_list;
vl->append(BuildConnVal());
vl->append(new Val(is_orig, TYPE_BOOL));
vl->append(new StringVal(len, (const char*) data));
ConnectionEvent(udp_contents, vl);
}
}
if ( is_orig )
{
Conn()->CheckHistory(HIST_ORIG_DATA_PKT, 'D');
if ( request_len < 0 )
request_len = ulen;
else
{
request_len += ulen;
#ifdef DEBUG
if ( request_len < 0 )
reporter->Warning("wrapping around for UDP request length");
#endif
}
Event(udp_request);
}
else
{
Conn()->CheckHistory(HIST_RESP_DATA_PKT, 'd');
if ( reply_len < 0 )
reply_len = ulen;
else
{
reply_len += ulen;
#ifdef DEBUG
if ( reply_len < 0 )
reporter->Warning("wrapping around for UDP reply length");
#endif
}
Event(udp_reply);
}
if ( caplen >= len )
ForwardPacket(len, data, is_orig, seq, ip, caplen);
}
void UDP_Analyzer::UpdateConnVal(RecordVal *conn_val)
{
RecordVal *orig_endp = conn_val->Lookup("orig")->AsRecordVal();
RecordVal *resp_endp = conn_val->Lookup("resp")->AsRecordVal();
UpdateEndpointVal(orig_endp, 1);
UpdateEndpointVal(resp_endp, 0);
// Call children's UpdateConnVal
Analyzer::UpdateConnVal(conn_val);
}
void UDP_Analyzer::UpdateEndpointVal(RecordVal* endp, int is_orig)
{
bro_int_t size = is_orig ? request_len : reply_len;
if ( size < 0 )
{
endp->Assign(0, new Val(0, TYPE_COUNT));
endp->Assign(1, new Val(int(UDP_INACTIVE), TYPE_COUNT));
}
else
{
endp->Assign(0, new Val(size, TYPE_COUNT));
endp->Assign(1, new Val(int(UDP_ACTIVE), TYPE_COUNT));
}
}
bool UDP_Analyzer::IsReuse(double /* t */, const u_char* /* pkt */)
{
return 0;
}
unsigned int UDP_Analyzer::MemoryAllocation() const
{
// A rather low lower bound....
return Analyzer::MemoryAllocation() + padded_sizeof(*this) - 24;
}
bool UDP_Analyzer::ValidateChecksum(const IP_Hdr* ip, const udphdr* up, int len)
{
uint32 sum;
if ( len % 2 == 1 )
// Add in pad byte.
sum = htons(((const u_char*) up)[len - 1] << 8);
else
sum = 0;
sum = ones_complement_checksum(ip->SrcAddr(), sum);
sum = ones_complement_checksum(ip->DstAddr(), sum);
// Note, for IPv6, strictly speaking the protocol and length fields are
// 32 bits rather than 16 bits. But because the upper bits are all zero,
// we get the same checksum either way.
sum += htons(IPPROTO_UDP);
sum += htons((unsigned short) len);
sum = ones_complement_checksum((void*) up, len, sum);
return sum == 0xffff;
}