zeek/src/session/Manager.cc
Arne Welzel 17836ef7d9 session/Manager: Emit explicit errors for FindConnection() with proto=65535
We silently broke users constructing conn_id records manually and
subsequently using them with lookup_connection() or connection_exists().

This is an attempt to at least report a runtime error about the situation
so it doesn't go completely unnoticed.
2025-01-17 17:57:49 +01:00

261 lines
7.8 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#include "zeek/session/Manager.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pcap.h>
#include <unistd.h>
#include <cstdlib>
#include "zeek/Conn.h"
#include "zeek/Func.h"
#include "zeek/IP.h"
#include "zeek/NetVar.h"
#include "zeek/Reporter.h"
#include "zeek/RuleMatcher.h"
#include "zeek/RunState.h"
#include "zeek/Timer.h"
#include "zeek/TunnelEncapsulation.h"
#include "zeek/packet_analysis/Manager.h"
#include "zeek/session/Session.h"
#include "zeek/telemetry/Manager.h"
#include "zeek/util.h"
zeek::session::Manager* zeek::session_mgr = nullptr;
namespace zeek::session {
namespace detail {
class ProtocolStats {
public:
struct Protocol {
std::shared_ptr<telemetry::Gauge> active;
std::shared_ptr<telemetry::Counter> total;
ssize_t max = 0;
Protocol(const std::shared_ptr<telemetry::GaugeFamily>& active_family,
const std::shared_ptr<telemetry::CounterFamily>& total_family, std::string protocol)
: active(active_family->GetOrAdd({{"protocol", protocol}})),
total(total_family->GetOrAdd({{"protocol", protocol}})) {}
};
using ProtocolMap = std::map<std::string, Protocol>;
ProtocolMap::iterator InitCounters(const std::string& protocol) {
auto active_family =
telemetry_mgr->GaugeFamily("zeek", "active_sessions", {"protocol"}, "Active Zeek Sessions");
auto total_family =
telemetry_mgr->CounterFamily("zeek", "total_sessions", {"protocol"}, "Total number of sessions");
auto [it, inserted] = entries.insert({protocol, Protocol{active_family, total_family, protocol}});
if ( inserted )
return it;
return entries.end();
}
Protocol* GetCounters(const std::string& protocol) {
auto it = entries.find(protocol);
if ( it == entries.end() )
it = InitCounters(protocol);
if ( it != entries.end() )
return &(it->second);
return nullptr;
}
private:
ProtocolMap entries;
};
} // namespace detail
Manager::Manager() {
stats = new detail::ProtocolStats();
ended_sessions_metric_family = telemetry_mgr->CounterFamily("zeek", "ended_sessions", {"reason"},
"Number of sessions ended for specific reasons");
ended_by_inactivity_metric = ended_sessions_metric_family->GetOrAdd({{"reason", "inactivity"}}, []() {
return static_cast<double>(zeek::detail::killed_by_inactivity);
});
}
Manager::~Manager() {
Clear();
delete stats;
}
void Manager::Done() {}
Connection* Manager::FindConnection(Val* v) {
zeek::detail::ConnKey conn_key(v);
if ( ! conn_key.Valid() ) {
// Produce a loud error for invalid script-layer conn_id records.
const char* extra = "";
if ( conn_key.transport == UNKNOWN_IP_PROTO )
extra = ": the proto field has the \"unknown\" 65535 value. Did you forget to set it?";
zeek::emit_builtin_error(zeek::util::fmt("invalid connection ID record encountered%s", extra));
return nullptr;
}
return FindConnection(conn_key);
}
Connection* Manager::FindConnection(const zeek::detail::ConnKey& conn_key) {
detail::Key key(&conn_key, sizeof(conn_key), detail::Key::CONNECTION_KEY_TYPE, false);
auto it = session_map.find(key);
if ( it != session_map.end() )
return static_cast<Connection*>(it->second);
return nullptr;
}
void Manager::Remove(Session* s) {
if ( s->IsInSessionTable() ) {
s->CancelTimers();
s->Done();
s->RemovalEvent();
detail::Key key = s->SessionKey(false);
if ( session_map.erase(key) == 0 )
reporter->InternalWarning("connection missing");
else {
Connection* c = static_cast<Connection*>(s);
if ( auto* stat_block = stats->GetCounters(c->TransportIdentifier()) )
stat_block->active->Dec();
}
// Mark that the session isn't in the table so that in case the
// session has been Ref()'d somewhere, we know that on a future
// call to Remove() that it's no longer in the map.
s->SetInSessionTable(false);
Unref(s);
}
}
void Manager::Insert(Session* s, bool remove_existing) {
Session* old = nullptr;
detail::Key key = s->SessionKey(true);
if ( remove_existing ) {
auto it = session_map.find(key);
if ( it != session_map.end() )
old = it->second;
session_map.erase(key);
}
InsertSession(std::move(key), s);
if ( old && old != s ) {
// Some clean-ups similar to those in Remove() (but invisible
// to the script layer).
old->CancelTimers();
old->SetInSessionTable(false);
Unref(old);
}
}
void Manager::Drain() {
// If a random seed was passed in, we're most likely in testing mode and need the
// order of the sessions to be consistent. Sort the keys to force that order
// every run.
if ( zeek::util::detail::have_random_seed() ) {
std::vector<const detail::Key*> keys;
keys.reserve(session_map.size());
for ( auto& entry : session_map )
keys.push_back(&(entry.first));
std::sort(keys.begin(), keys.end(), [](const detail::Key* a, const detail::Key* b) { return *a < *b; });
for ( const auto* k : keys ) {
Session* tc = session_map.at(*k);
tc->Done();
tc->RemovalEvent();
}
}
else {
for ( const auto& entry : session_map ) {
Session* tc = entry.second;
tc->Done();
tc->RemovalEvent();
}
}
}
void Manager::Clear() {
for ( const auto& entry : session_map )
Unref(entry.second);
session_map.clear();
zeek::detail::fragment_mgr->Clear();
}
void Manager::GetStats(Stats& s) {
auto* tcp_stats = stats->GetCounters("tcp");
s.max_TCP_conns = tcp_stats->max;
s.num_TCP_conns = tcp_stats->active->Value();
s.cumulative_TCP_conns = tcp_stats->total->Value();
auto* udp_stats = stats->GetCounters("udp");
s.max_UDP_conns = udp_stats->max;
s.num_UDP_conns = udp_stats->active->Value();
s.cumulative_UDP_conns = udp_stats->total->Value();
auto* icmp_stats = stats->GetCounters("icmp");
s.max_ICMP_conns = icmp_stats->max;
s.num_ICMP_conns = icmp_stats->active->Value();
s.cumulative_ICMP_conns = icmp_stats->total->Value();
s.num_fragments = zeek::detail::fragment_mgr->Size();
s.max_fragments = zeek::detail::fragment_mgr->MaxFragments();
s.num_packets = packet_mgr->PacketsProcessed();
}
void Manager::Weird(const char* name, const Packet* pkt, const char* addl, const char* source) {
const char* weird_name = name;
if ( pkt ) {
pkt->dump_packet = true;
if ( pkt->encap && pkt->encap->LastType() != BifEnum::Tunnel::NONE )
weird_name = util::fmt("%s_in_tunnel", name);
if ( pkt->ip_hdr ) {
reporter->Weird(pkt->ip_hdr->SrcAddr(), pkt->ip_hdr->DstAddr(), weird_name, addl, source);
return;
}
}
reporter->Weird(weird_name, addl, source);
}
void Manager::Weird(const char* name, const IP_Hdr* ip, const char* addl) {
reporter->Weird(ip->SrcAddr(), ip->DstAddr(), name, addl);
}
void Manager::InsertSession(detail::Key key, Session* session) {
session->SetInSessionTable(true);
key.CopyData();
session_map.insert_or_assign(std::move(key), session);
std::string protocol = session->TransportIdentifier();
if ( auto* stat_block = stats->GetCounters(protocol) ) {
stat_block->active->Inc();
stat_block->total->Inc();
if ( stat_block->active->Value() > stat_block->max )
stat_block->max++;
}
}
} // namespace zeek::session