zeek/src/analyzer/protocol/udp/UDP.cc
2020-04-16 16:44:33 -07:00

279 lines
6.8 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#include <algorithm>
#include "zeek-config.h"
#include "Net.h"
#include "NetVar.h"
#include "analyzer/protocol/udp/UDP.h"
#include "analyzer/Manager.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"
req_chk_cnt = rep_chk_cnt = 0;
req_chk_thresh = rep_chk_thresh = 1;
}
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_t 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, std::min(len, caplen) - sizeof(struct udphdr));
int chksum = up->uh_sum;
auto validate_checksum = ! current_pkt->l3_checksummed && ! ignore_checksums && caplen >=len;
constexpr auto vxlan_len = 8;
constexpr auto eth_len = 14;
if ( validate_checksum &&
len > ((int)sizeof(struct udphdr) + vxlan_len + eth_len) &&
(data[0] & 0x08) == 0x08 )
{
auto& vxlan_ports = analyzer_mgr->GetVxlanPorts();
if ( std::find(vxlan_ports.begin(), vxlan_ports.end(),
ntohs(up->uh_dport)) != vxlan_ports.end() )
{
// Looks like VXLAN on a well-known port, so the checksum should be
// transmitted as zero, and we should accept that. If not
// transmitted as zero, then validating the checksum is optional.
if ( chksum == 0 )
validate_checksum = false;
else
validate_checksum = BifConst::Tunnel::validate_vxlan_checksums;
}
}
if ( validate_checksum )
{
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 )
{
uint32_t t = req_chk_thresh;
if ( Conn()->ScaledHistoryEntry('C', req_chk_cnt,
req_chk_thresh) )
ChecksumEvent(is_orig, t);
}
else
{
uint32_t t = rep_chk_thresh;
if ( Conn()->ScaledHistoryEntry('c', rep_chk_cnt,
rep_chk_thresh) )
ChecksumEvent(is_orig, t);
}
return;
}
}
int ulen = ntohs(up->uh_ulen);
if ( ulen != len )
Weird("UDP_datagram_length_mismatch", fmt("%d != %d", ulen, len));
len -= sizeof(struct udphdr);
ulen -= sizeof(struct udphdr);
caplen -= sizeof(struct udphdr);
Conn()->SetLastTime(current_timestamp);
if ( udp_contents )
{
bool do_udp_contents = false;
auto sport_val = IntrusivePtr{AdoptRef{}, val_mgr->GetPort(ntohs(up->uh_sport), TRANSPORT_UDP)};
auto dport_val = IntrusivePtr{AdoptRef{}, val_mgr->GetPort(ntohs(up->uh_dport), TRANSPORT_UDP)};
if ( udp_content_ports->Lookup(dport_val.get()) ||
udp_content_ports->Lookup(sport_val.get()) )
do_udp_contents = true;
else
{
uint16_t p = udp_content_delivery_ports_use_resp ? Conn()->RespPort()
: up->uh_dport;
auto port_val = IntrusivePtr{AdoptRef{}, val_mgr->GetPort(ntohs(p), TRANSPORT_UDP)};
if ( is_orig )
{
auto result = udp_content_delivery_ports_orig->Lookup(port_val.get());
if ( udp_content_deliver_all_orig || (result && result->AsBool()) )
do_udp_contents = true;
}
else
{
auto result = udp_content_delivery_ports_resp->Lookup(port_val.get());
if ( udp_content_deliver_all_resp || (result && result->AsBool()) )
do_udp_contents = true;
}
}
if ( do_udp_contents )
EnqueueConnEvent(udp_contents,
IntrusivePtr{AdoptRef{}, BuildConnVal()},
val_mgr->Bool(is_orig),
make_intrusive<StringVal>(len, (const char*) data)
);
}
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, true);
UpdateEndpointVal(resp_endp, false);
// Call children's UpdateConnVal
Analyzer::UpdateConnVal(conn_val);
}
void UDP_Analyzer::UpdateEndpointVal(RecordVal* endp, bool is_orig)
{
bro_int_t size = is_orig ? request_len : reply_len;
if ( size < 0 )
{
endp->Assign(0, val_mgr->GetCount(0));
endp->Assign(1, val_mgr->GetCount(int(UDP_INACTIVE)));
}
else
{
endp->Assign(0, val_mgr->GetCount(size));
endp->Assign(1, val_mgr->GetCount(int(UDP_ACTIVE)));
}
}
bool UDP_Analyzer::IsReuse(double /* t */, const u_char* /* pkt */)
{
return false;
}
unsigned int UDP_Analyzer::MemoryAllocation() const
{
// A rather low lower bound....
return Analyzer::MemoryAllocation() + padded_sizeof(*this) - 24;
}
void UDP_Analyzer::ChecksumEvent(bool is_orig, uint32_t threshold)
{
Conn()->HistoryThresholdEvent(udp_multiple_checksum_errors,
is_orig, threshold);
}
bool UDP_Analyzer::ValidateChecksum(const IP_Hdr* ip, const udphdr* up, int len)
{
uint32_t 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;
}