zeek/src/analyzer/Manager.cc
Jon Siwek 09ae539ea8 GH-250: Improve/cleanup VXLAN decapsulation support
* Better parsing/error-checking of VXLAN and encapsulated packet headers

* Add/implement the "vxlan_packet" event

* Add "Tunnel::vxlan_ports" option to tune the set of VXLAN ports to
  analyze/decapsulate

* Add "Tunnel::validate_vxlan_checksums" option to allow for tuning of how
  checksums associated with the outer UDP header of a possible VXLAN
  tunnel are handled

Fixes GH-250
2019-03-12 18:15:34 -07:00

655 lines
16 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#include "Manager.h"
#include "Hash.h"
#include "Val.h"
#include "protocol/backdoor/BackDoor.h"
#include "protocol/conn-size/ConnSize.h"
#include "protocol/icmp/ICMP.h"
#include "protocol/interconn/InterConn.h"
#include "protocol/pia/PIA.h"
#include "protocol/stepping-stone/SteppingStone.h"
#include "protocol/tcp/TCP.h"
#include "protocol/udp/UDP.h"
#include "plugin/Manager.h"
#include "protocol/tcp/events.bif.h"
using namespace analyzer;
Manager::ConnIndex::ConnIndex(const IPAddr& _orig, const IPAddr& _resp,
uint16 _resp_p, uint16 _proto)
{
if ( _orig == IPAddr(string("0.0.0.0")) )
// don't use the IPv4 mapping, use the literal unspecified address
// to indicate a wildcard
orig = IPAddr(string("::"));
else
orig = _orig;
resp = _resp;
resp_p = _resp_p;
proto = _proto;
}
Manager::ConnIndex::ConnIndex()
{
orig = resp = IPAddr("0.0.0.0");
resp_p = 0;
proto = 0;
}
bool Manager::ConnIndex::operator<(const ConnIndex& other) const
{
if ( orig != other.orig )
return orig < other.orig;
if ( resp != other.resp )
return resp < other.resp;
if ( proto != other.proto )
return proto < other.proto;
if ( resp_p != other.resp_p )
return resp_p < other.resp_p;
return false;
}
Manager::Manager()
: plugin::ComponentManager<analyzer::Tag, analyzer::Component>("Analyzer", "Tag")
{
}
Manager::~Manager()
{
for ( analyzer_map_by_port::const_iterator i = analyzers_by_port_tcp.begin(); i != analyzers_by_port_tcp.end(); i++ )
delete i->second;
for ( analyzer_map_by_port::const_iterator i = analyzers_by_port_udp.begin(); i != analyzers_by_port_udp.end(); i++ )
delete i->second;
analyzers_by_port_udp.clear();
analyzers_by_port_tcp.clear();
// Clean up expected-connection table.
while ( conns_by_timeout.size() )
{
ScheduledAnalyzer* a = conns_by_timeout.top();
conns_by_timeout.pop();
delete a;
}
}
void Manager::InitPreScript()
{
// Cache these tags.
analyzer_backdoor = GetComponentTag("BACKDOOR");
analyzer_connsize = GetComponentTag("CONNSIZE");
analyzer_interconn = GetComponentTag("INTERCONN");
analyzer_stepping = GetComponentTag("STEPPINGSTONE");
analyzer_tcpstats = GetComponentTag("TCPSTATS");
}
void Manager::InitPostScript()
{
auto id = global_scope()->Lookup("Tunnel::vxlan_ports");
if ( ! (id && id->ID_Val()) )
reporter->FatalError("Tunnel::vxlan_ports not defined");
auto table_val = id->ID_Val()->AsTableVal();
auto port_list = table_val->ConvertToPureList();
for ( auto i = 0; i < port_list->Length(); ++i )
vxlan_ports.emplace_back(port_list->Index(i)->AsPortVal()->Port());
Unref(port_list);
}
void Manager::DumpDebug()
{
#ifdef DEBUG
DBG_LOG(DBG_ANALYZER, "Available analyzers after bro_init():");
list<Component*> all_analyzers = GetComponents();
for ( list<Component*>::const_iterator i = all_analyzers.begin(); i != all_analyzers.end(); ++i )
DBG_LOG(DBG_ANALYZER, " %s (%s)", (*i)->Name().c_str(),
IsEnabled((*i)->Tag()) ? "enabled" : "disabled");
DBG_LOG(DBG_ANALYZER, " ");
DBG_LOG(DBG_ANALYZER, "Analyzers by port:");
for ( analyzer_map_by_port::const_iterator i = analyzers_by_port_tcp.begin(); i != analyzers_by_port_tcp.end(); i++ )
{
string s;
for ( tag_set::const_iterator j = i->second->begin(); j != i->second->end(); j++ )
s += string(GetComponentName(*j)) + " ";
DBG_LOG(DBG_ANALYZER, " %d/tcp: %s", i->first, s.c_str());
}
for ( analyzer_map_by_port::const_iterator i = analyzers_by_port_udp.begin(); i != analyzers_by_port_udp.end(); i++ )
{
string s;
for ( tag_set::const_iterator j = i->second->begin(); j != i->second->end(); j++ )
s += string(GetComponentName(*j)) + " ";
DBG_LOG(DBG_ANALYZER, " %d/udp: %s", i->first, s.c_str());
}
#endif
}
void Manager::Done()
{
}
bool Manager::EnableAnalyzer(Tag tag)
{
Component* p = Lookup(tag);
if ( ! p )
return false;
DBG_LOG(DBG_ANALYZER, "Enabling analyzer %s", p->Name().c_str());
p->SetEnabled(true);
return true;
}
bool Manager::EnableAnalyzer(EnumVal* val)
{
Component* p = Lookup(val);
if ( ! p )
return false;
DBG_LOG(DBG_ANALYZER, "Enabling analyzer %s", p->Name().c_str());
p->SetEnabled(true);
return true;
}
bool Manager::DisableAnalyzer(Tag tag)
{
Component* p = Lookup(tag);
if ( ! p )
return false;
DBG_LOG(DBG_ANALYZER, "Disabling analyzer %s", p->Name().c_str());
p->SetEnabled(false);
return true;
}
bool Manager::DisableAnalyzer(EnumVal* val)
{
Component* p = Lookup(val);
if ( ! p )
return false;
DBG_LOG(DBG_ANALYZER, "Disabling analyzer %s", p->Name().c_str());
p->SetEnabled(false);
return true;
}
void Manager::DisableAllAnalyzers()
{
DBG_LOG(DBG_ANALYZER, "Disabling all analyzers");
list<Component*> all_analyzers = GetComponents();
for ( list<Component*>::const_iterator i = all_analyzers.begin(); i != all_analyzers.end(); ++i )
(*i)->SetEnabled(false);
}
analyzer::Tag Manager::GetAnalyzerTag(const char* name)
{
return GetComponentTag(name);
}
bool Manager::IsEnabled(Tag tag)
{
if ( ! tag )
return false;
Component* p = Lookup(tag);
if ( ! p )
return false;
return p->Enabled();
}
bool Manager::IsEnabled(EnumVal* val)
{
Component* p = Lookup(val);
if ( ! p )
return false;
return p->Enabled();
}
bool Manager::RegisterAnalyzerForPort(EnumVal* val, PortVal* port)
{
Component* p = Lookup(val);
if ( ! p )
return false;
return RegisterAnalyzerForPort(p->Tag(), port->PortType(), port->Port());
}
bool Manager::UnregisterAnalyzerForPort(EnumVal* val, PortVal* port)
{
Component* p = Lookup(val);
if ( ! p )
return false;
return UnregisterAnalyzerForPort(p->Tag(), port->PortType(), port->Port());
}
bool Manager::RegisterAnalyzerForPort(Tag tag, TransportProto proto, uint32 port)
{
tag_set* l = LookupPort(proto, port, true);
if ( ! l )
return false;
#ifdef DEBUG
const char* name = GetComponentName(tag).c_str();
DBG_LOG(DBG_ANALYZER, "Registering analyzer %s for port %" PRIu32 "/%d", name, port, proto);
#endif
l->insert(tag);
return true;
}
bool Manager::UnregisterAnalyzerForPort(Tag tag, TransportProto proto, uint32 port)
{
tag_set* l = LookupPort(proto, port, true);
if ( ! l )
return true; // still a "successful" unregistration
#ifdef DEBUG
const char* name = GetComponentName(tag).c_str();
DBG_LOG(DBG_ANALYZER, "Unregistering analyzer %s for port %" PRIu32 "/%d", name, port, proto);
#endif
l->erase(tag);
return true;
}
Analyzer* Manager::InstantiateAnalyzer(Tag tag, Connection* conn)
{
Component* c = Lookup(tag);
if ( ! c )
{
reporter->InternalWarning("request to instantiate unknown analyzer");
return 0;
}
if ( ! c->Enabled() )
return 0;
if ( ! c->Factory() )
{
reporter->InternalWarning("analyzer %s cannot be instantiated dynamically",
GetComponentName(tag).c_str());
return 0;
}
Analyzer* a = c->Factory()(conn);
if ( ! a )
{
reporter->InternalWarning("analyzer instantiation failed");
return 0;
}
a->SetAnalyzerTag(tag);
return a;
}
Analyzer* Manager::InstantiateAnalyzer(const char* name, Connection* conn)
{
Tag tag = GetComponentTag(name);
return tag ? InstantiateAnalyzer(tag, conn) : 0;
}
Manager::tag_set* Manager::LookupPort(TransportProto proto, uint32 port, bool add_if_not_found)
{
analyzer_map_by_port* m = 0;
switch ( proto ) {
case TRANSPORT_TCP:
m = &analyzers_by_port_tcp;
break;
case TRANSPORT_UDP:
m = &analyzers_by_port_udp;
break;
default:
reporter->InternalWarning("unsupported transport protocol in analyzer::Manager::LookupPort");
return 0;
}
analyzer_map_by_port::const_iterator i = m->find(port);
if ( i != m->end() )
return i->second;
if ( ! add_if_not_found )
return 0;
tag_set* l = new tag_set;
m->insert(std::make_pair(port, l));
return l;
}
Manager::tag_set* Manager::LookupPort(PortVal* val, bool add_if_not_found)
{
return LookupPort(val->PortType(), val->Port(), add_if_not_found);
}
bool Manager::BuildInitialAnalyzerTree(Connection* conn)
{
tcp::TCP_Analyzer* tcp = 0;
udp::UDP_Analyzer* udp = 0;
icmp::ICMP_Analyzer* icmp = 0;
TransportLayerAnalyzer* root = 0;
pia::PIA* pia = 0;
bool check_port = false;
switch ( conn->ConnTransport() ) {
case TRANSPORT_TCP:
root = tcp = new tcp::TCP_Analyzer(conn);
pia = new pia::PIA_TCP(conn);
check_port = true;
DBG_ANALYZER(conn, "activated TCP analyzer");
break;
case TRANSPORT_UDP:
root = udp = new udp::UDP_Analyzer(conn);
pia = new pia::PIA_UDP(conn);
check_port = true;
DBG_ANALYZER(conn, "activated UDP analyzer");
break;
case TRANSPORT_ICMP: {
root = icmp = new icmp::ICMP_Analyzer(conn);
DBG_ANALYZER(conn, "activated ICMP analyzer");
break;
}
default:
reporter->InternalWarning("unknown protocol can't build analyzer tree");
return false;
}
bool scheduled = ApplyScheduledAnalyzers(conn, false, root);
// Hmm... Do we want *just* the expected analyzer, or all
// other potential analyzers as well? For now we only take
// the scheduled ones.
if ( ! scheduled )
{ // Let's see if it's a port we know.
if ( check_port && ! dpd_ignore_ports )
{
int resp_port = ntohs(conn->RespPort());
tag_set* ports = LookupPort(conn->ConnTransport(), resp_port, false);
if ( ports )
{
for ( tag_set::const_iterator j = ports->begin(); j != ports->end(); ++j )
{
Analyzer* analyzer = analyzer_mgr->InstantiateAnalyzer(*j, conn);
if ( ! analyzer )
continue;
root->AddChildAnalyzer(analyzer, false);
DBG_ANALYZER_ARGS(conn, "activated %s analyzer due to port %d",
analyzer_mgr->GetComponentName(*j).c_str(), resp_port);
}
}
}
}
if ( tcp )
{
// We have to decide whether to reassamble the stream.
// We turn it on right away if we already have an app-layer
// analyzer, reassemble_first_packets is true, or the user
// asks us to do so. In all other cases, reassembly may
// be turned on later by the TCP PIA.
bool reass = root->GetChildren().size() ||
dpd_reassemble_first_packets ||
tcp_content_deliver_all_orig ||
tcp_content_deliver_all_resp;
if ( tcp_contents && ! reass )
{
auto dport = val_mgr->GetPort(ntohs(conn->RespPort()), TRANSPORT_TCP);
Val* result;
if ( ! reass )
reass = tcp_content_delivery_ports_orig->Lookup(dport);
if ( ! reass )
reass = tcp_content_delivery_ports_resp->Lookup(dport);
Unref(dport);
}
if ( reass )
tcp->EnableReassembly();
if ( IsEnabled(analyzer_backdoor) )
// Add a BackDoor analyzer if requested. This analyzer
// can handle both reassembled and non-reassembled input.
tcp->AddChildAnalyzer(new backdoor::BackDoor_Analyzer(conn), false);
if ( IsEnabled(analyzer_interconn) )
// Add a InterConn analyzer if requested. This analyzer
// can handle both reassembled and non-reassembled input.
tcp->AddChildAnalyzer(new interconn::InterConn_Analyzer(conn), false);
if ( IsEnabled(analyzer_stepping) )
{
// Add a SteppingStone analyzer if requested. The port
// should really not be hardcoded here, but as it can
// handle non-reassembled data, it doesn't really fit into
// our general framing ... Better would be to turn it
// on *after* we discover we have interactive traffic.
uint16 resp_port = ntohs(conn->RespPort());
if ( resp_port == 22 || resp_port == 23 || resp_port == 513 )
{
AddrVal src(conn->OrigAddr());
if ( ! stp_skip_src->Lookup(&src) )
tcp->AddChildAnalyzer(new stepping_stone::SteppingStone_Analyzer(conn), false);
}
}
if ( IsEnabled(analyzer_tcpstats) )
// Add TCPStats analyzer. This needs to see packets so
// we cannot add it as a normal child.
tcp->AddChildPacketAnalyzer(new tcp::TCPStats_Analyzer(conn));
if ( IsEnabled(analyzer_connsize) )
// Add ConnSize analyzer. Needs to see packets, not stream.
tcp->AddChildPacketAnalyzer(new conn_size::ConnSize_Analyzer(conn));
}
else
{
if ( IsEnabled(analyzer_connsize) )
// Add ConnSize analyzer. Needs to see packets, not stream.
root->AddChildAnalyzer(new conn_size::ConnSize_Analyzer(conn));
}
if ( pia )
root->AddChildAnalyzer(pia->AsAnalyzer());
conn->SetRootAnalyzer(root, pia);
root->Init();
root->InitChildren();
PLUGIN_HOOK_VOID(HOOK_SETUP_ANALYZER_TREE, HookSetupAnalyzerTree(conn));
return true;
}
void Manager::ExpireScheduledAnalyzers()
{
if ( ! network_time )
return;
while ( conns_by_timeout.size() )
{
ScheduledAnalyzer* a = conns_by_timeout.top();
if ( a->timeout > network_time )
return;
conns_by_timeout.pop();
std::pair<conns_map::iterator, conns_map::iterator> all = conns.equal_range(a->conn);
bool found = false;
for ( conns_map::iterator i = all.first; i != all.second; i++ )
{
if ( i->second != a )
continue;
conns.erase(i);
DBG_LOG(DBG_ANALYZER, "Expiring expected analyzer %s for connection %s",
analyzer_mgr->GetComponentName(a->analyzer).c_str(),
fmt_conn_id(a->conn.orig, 0, a->conn.resp, a->conn.resp_p));
delete a;
found = true;
break;
}
assert(found);
}
}
void Manager::ScheduleAnalyzer(const IPAddr& orig, const IPAddr& resp,
uint16 resp_p,
TransportProto proto, Tag analyzer,
double timeout)
{
if ( ! network_time )
{
reporter->Warning("cannot schedule analyzers before processing begins; ignored");
return;
}
assert(timeout);
// Use the chance to see if the oldest entry is already expired.
ExpireScheduledAnalyzers();
ScheduledAnalyzer* a = new ScheduledAnalyzer;
a->conn = ConnIndex(orig, resp, resp_p, proto);
a->analyzer = analyzer;
a->timeout = network_time + timeout;
conns.insert(std::make_pair(a->conn, a));
conns_by_timeout.push(a);
}
void Manager::ScheduleAnalyzer(const IPAddr& orig, const IPAddr& resp,
uint16 resp_p,
TransportProto proto, const char* analyzer,
double timeout)
{
Tag tag = GetComponentTag(analyzer);
if ( tag != Tag() )
ScheduleAnalyzer(orig, resp, resp_p, proto, tag, timeout);
}
void Manager::ScheduleAnalyzer(const IPAddr& orig, const IPAddr& resp, PortVal* resp_p,
Val* analyzer, double timeout)
{
EnumVal* ev = analyzer->AsEnumVal();
return ScheduleAnalyzer(orig, resp, resp_p->Port(), resp_p->PortType(), Tag(ev), timeout);
}
Manager::tag_set Manager::GetScheduled(const Connection* conn)
{
ConnIndex c(conn->OrigAddr(), conn->RespAddr(),
ntohs(conn->RespPort()), conn->ConnTransport());
std::pair<conns_map::iterator, conns_map::iterator> all = conns.equal_range(c);
tag_set result;
for ( conns_map::iterator i = all.first; i != all.second; i++ )
result.insert(i->second->analyzer);
// Try wildcard for originator.
c.orig = IPAddr(string("::"));
all = conns.equal_range(c);
for ( conns_map::iterator i = all.first; i != all.second; i++ )
{
if ( i->second->timeout > network_time )
result.insert(i->second->analyzer);
}
// We don't delete scheduled analyzers here. They will be expired
// eventually.
return result;
}
bool Manager::ApplyScheduledAnalyzers(Connection* conn, bool init, TransportLayerAnalyzer* parent)
{
if ( ! parent )
parent = conn->GetRootAnalyzer();
if ( ! parent )
return false;
tag_set expected = GetScheduled(conn);
for ( tag_set::iterator it = expected.begin(); it != expected.end(); ++it )
{
Analyzer* analyzer = analyzer_mgr->InstantiateAnalyzer(*it, conn);
if ( ! analyzer )
continue;
parent->AddChildAnalyzer(analyzer, init);
EnumVal* tag = it->AsEnumVal();
Ref(tag);
conn->Event(scheduled_analyzer_applied, 0, tag);
DBG_ANALYZER_ARGS(conn, "activated %s analyzer as scheduled",
analyzer_mgr->GetComponentName(*it).c_str());
}
return expected.size();
}