diff --git a/src/Conn.cc b/src/Conn.cc index 965b17a4b0..c242d3c9d1 100644 --- a/src/Conn.cc +++ b/src/Conn.cc @@ -54,12 +54,13 @@ uint64_t Connection::total_connections = 0; uint64_t Connection::current_connections = 0; uint64_t Connection::external_connections = 0; -Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id, +Connection::Connection(NetSessions* s, const ConnIDKey& k, double t, const ConnID* id, uint32_t flow, const Packet* pkt, - const EncapsulationStack* arg_encap) + const EncapsulationStack* arg_encap) { sessions = s; key = k; + key_valid = true; start_time = last_time = t; orig_addr = id->src_addr; @@ -144,7 +145,6 @@ Connection::~Connection() Unref(conn_val); } - delete key; delete root_analyzer; delete conn_timer_mgr; delete encapsulation; @@ -520,7 +520,7 @@ void Connection::AddTimer(timer_func timer, double t, int do_expire, // If the key is cleared, the connection isn't stored in the connection // table anymore and will soon be deleted. We're not installing new // timers anymore then. - if ( ! key ) + if ( ! key_valid ) return; Timer* conn_timer = new ConnectionTimer(this, timer, t, do_expire, type); @@ -600,7 +600,6 @@ void Connection::FlipRoles() unsigned int Connection::MemoryAllocation() const { return padded_sizeof(*this) - + (key ? key->MemoryAllocation() : 0) + (timers.MemoryAllocation() - padded_sizeof(timers)) + (conn_val ? conn_val->MemoryAllocation() : 0) + (root_analyzer ? root_analyzer->MemoryAllocation(): 0) diff --git a/src/Conn.h b/src/Conn.h index 836144eedc..d0c73d93d5 100644 --- a/src/Conn.h +++ b/src/Conn.h @@ -5,7 +5,6 @@ #include -#include #include #include "Dict.h" @@ -57,7 +56,7 @@ namespace analyzer { class Analyzer; } class Connection : public BroObj { public: - Connection(NetSessions* s, HashKey* k, double t, const ConnID* id, + Connection(NetSessions* s, const ConnIDKey& k, double t, const ConnID* id, uint32_t flow, const Packet* pkt, const EncapsulationStack* arg_encap); ~Connection() override; @@ -90,11 +89,15 @@ public: // arguments for reproducing packets const Packet *pkt); - HashKey* Key() const { return key; } - void ClearKey() { key = 0; } + // Keys are only considered valid for a connection when a + // connection is in the session map. If it is removed, the key + // should be marked invalid. + const ConnIDKey& Key() const { return key; } + void ClearKey() { key_valid = false; } + bool IsKeyValid() const { return key_valid; } double StartTime() const { return start_time; } - void SetStartTime(double t) { start_time = t; } + void SetStartTime(double t) { start_time = t; } double LastTime() const { return last_time; } void SetLastTime(double t) { last_time = t; } @@ -306,7 +309,8 @@ protected: void RemoveConnectionTimer(double t); NetSessions* sessions; - HashKey* key; + ConnIDKey key; + bool key_valid; // Timer manager to use for this conn (or nil). TimerMgr::Tag* conn_timer_mgr; diff --git a/src/IPAddr.cc b/src/IPAddr.cc index 4d4b11ce37..d9fe0edb5f 100644 --- a/src/IPAddr.cc +++ b/src/IPAddr.cc @@ -14,14 +14,9 @@ const uint8_t IPAddr::v4_mapped_prefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; -HashKey* BuildConnIDHashKey(const ConnID& id) +ConnIDKey BuildConnIDKey(const ConnID& id) { - struct { - in6_addr ip1; - in6_addr ip2; - uint16_t port1; - uint16_t port2; - } key; + ConnIDKey key; // Lookup up connection based on canonical ordering, which is // the smaller of and @@ -43,7 +38,7 @@ HashKey* BuildConnIDHashKey(const ConnID& id) key.port2 = id.src_port; } - return new HashKey(&key, sizeof(key)); + return key; } static inline uint32_t bit_mask32(int bottom_bits) diff --git a/src/IPAddr.h b/src/IPAddr.h index 1b59a0edfc..ab34e035d2 100644 --- a/src/IPAddr.h +++ b/src/IPAddr.h @@ -5,6 +5,7 @@ #include #include +#include #include #include "BroString.h" @@ -18,6 +19,31 @@ namespace analyzer { class ExpectedConn; } typedef in_addr in4_addr; +struct ConnIDKey + { + in6_addr ip1; + in6_addr ip2; + uint16_t port1; + uint16_t port2; + + ConnIDKey() : port1(0), port2(0) + { + memset(&ip1, 0, sizeof(in6_addr)); + memset(&ip2, 0, sizeof(in6_addr)); + } + + bool operator<(const ConnIDKey& rhs) const { return memcmp(this, &rhs, sizeof(ConnIDKey)) < 0; } + bool operator==(const ConnIDKey& rhs) const { return memcmp(this, &rhs, sizeof(ConnIDKey)) == 0; } + + ConnIDKey& operator=(const ConnIDKey& rhs) + { + if ( this != &rhs ) + memcpy(this, &rhs, sizeof(ConnIDKey)); + + return *this; + } + }; + /** * Class storing both IPv4 and IPv6 addresses. */ @@ -362,7 +388,7 @@ public: */ void ConvertToThreadingValue(threading::Value::addr_t* v) const; - friend HashKey* BuildConnIDHashKey(const ConnID& id); + friend ConnIDKey BuildConnIDKey(const ConnID& id); unsigned int MemoryAllocation() const { return padded_sizeof(*this); } @@ -485,9 +511,9 @@ inline void IPAddr::ConvertToThreadingValue(threading::Value::addr_t* v) const } /** - * Returns a hash key for a given ConnID. Passes ownership to caller. + * Returns a map key for a given ConnID. */ -HashKey* BuildConnIDHashKey(const ConnID& id); +ConnIDKey BuildConnIDKey(const ConnID& id); /** * Class storing both IPv4 and IPv6 prefixes diff --git a/src/Sessions.cc b/src/Sessions.cc index 3acc3be15f..4ca4ec6ef6 100644 --- a/src/Sessions.cc +++ b/src/Sessions.cc @@ -96,9 +96,6 @@ NetSessions::NetSessions() Unref(t); - tcp_conns.SetDeleteFunc(bro_obj_delete_func); - udp_conns.SetDeleteFunc(bro_obj_delete_func); - icmp_conns.SetDeleteFunc(bro_obj_delete_func); fragments.SetDeleteFunc(bro_obj_delete_func); if ( stp_correlate_pair ) @@ -128,6 +125,8 @@ NetSessions::NetSessions() arp_analyzer = new analyzer::arp::ARP_Analyzer(); else arp_analyzer = 0; + + memset(&stats, 0, sizeof(SessionStats)); } NetSessions::~NetSessions() @@ -138,6 +137,13 @@ NetSessions::~NetSessions() Unref(arp_analyzer); delete discarder; delete stp_manager; + + for ( const auto& entry : tcp_conns ) + Unref(entry.second); + for ( const auto& entry : udp_conns ) + Unref(entry.second); + for ( const auto& entry : icmp_conns ) + Unref(entry.second); } void NetSessions::Done() @@ -427,7 +433,7 @@ void NetSessions::DoNextPacket(double t, const Packet* pkt, const IP_Hdr* ip_hdr ConnID id; id.src_addr = ip_hdr->SrcAddr(); id.dst_addr = ip_hdr->DstAddr(); - Dictionary* d = 0; + ConnectionMap* d = nullptr; BifEnum::Tunnel::Type tunnel_type = BifEnum::Tunnel::IP; switch ( proto ) { @@ -715,30 +721,27 @@ void NetSessions::DoNextPacket(double t, const Packet* pkt, const IP_Hdr* ip_hdr return; } - HashKey* h = BuildConnIDHashKey(id); - if ( ! h ) - reporter->InternalError("hash computation failed"); - - Connection* conn = 0; + ConnIDKey key = BuildConnIDKey(id); + Connection* conn = nullptr; // FIXME: The following is getting pretty complex. Need to split up // into separate functions. - conn = (Connection*) d->Lookup(h); + auto it = d->find(key); + if ( it != d->end() ) + conn = it->second; + if ( ! conn ) { - conn = NewConn(h, t, &id, data, proto, ip_hdr->FlowLabel(), pkt, encapsulation); + conn = NewConn(key, t, &id, data, proto, ip_hdr->FlowLabel(), pkt, encapsulation); if ( conn ) - d->Insert(h, conn); + InsertConnection(d, key, conn); } else { // We already know that connection. int consistent = CheckConnectionTag(conn); if ( consistent < 0 ) - { - delete h; return; - } if ( ! consistent || conn->IsReuse(t, data) ) { @@ -746,22 +749,18 @@ void NetSessions::DoNextPacket(double t, const Packet* pkt, const IP_Hdr* ip_hdr conn->Event(connection_reused, 0); Remove(conn); - conn = NewConn(h, t, &id, data, proto, ip_hdr->FlowLabel(), pkt, encapsulation); + conn = NewConn(key, t, &id, data, proto, ip_hdr->FlowLabel(), pkt, encapsulation); if ( conn ) - d->Insert(h, conn); + InsertConnection(d, key, conn); } else { - delete h; conn->CheckEncapsulation(encapsulation); } } if ( ! conn ) - { - delete h; return; - } int record_packet = 1; // whether to record the packet at all int record_content = 1; // whether to record its data @@ -1016,11 +1015,8 @@ Connection* NetSessions::FindConnection(Val* v) id.is_one_way = 0; // ### incorrect for ICMP connections - HashKey* h = BuildConnIDHashKey(id); - if ( ! h ) - reporter->InternalError("hash computation failed"); - - Dictionary* d; + ConnIDKey key = BuildConnIDKey(id); + ConnectionMap* d; if ( orig_portv->IsTCP() ) d = &tcp_conns; @@ -1033,22 +1029,22 @@ Connection* NetSessions::FindConnection(Val* v) // This can happen due to pseudo-connections we // construct, for example for packet headers embedded // in ICMPs. - delete h; return 0; } - Connection* conn = (Connection*) d->Lookup(h); - - delete h; + Connection* conn = nullptr; + auto it = d->find(key); + if ( it != d->end() ) + conn = it->second; return conn; } void NetSessions::Remove(Connection* c) { - HashKey* k = c->Key(); - if ( k ) + if ( c->IsKeyValid() ) { + const ConnIDKey& key = c->Key(); c->CancelTimers(); if ( c->ConnTransport() == TRANSPORT_TCP ) @@ -1073,17 +1069,17 @@ void NetSessions::Remove(Connection* c) switch ( c->ConnTransport() ) { case TRANSPORT_TCP: - if ( ! tcp_conns.RemoveEntry(k) ) + if ( tcp_conns.erase(key) == 0 ) reporter->InternalWarning("connection missing"); break; case TRANSPORT_UDP: - if ( ! udp_conns.RemoveEntry(k) ) + if ( udp_conns.erase(key) == 0 ) reporter->InternalWarning("connection missing"); break; case TRANSPORT_ICMP: - if ( ! icmp_conns.RemoveEntry(k) ) + if ( icmp_conns.erase(key) == 0 ) reporter->InternalWarning("connection missing"); break; @@ -1093,7 +1089,6 @@ void NetSessions::Remove(Connection* c) } Unref(c); - delete k; } } @@ -1117,27 +1112,30 @@ void NetSessions::Remove(FragReassembler* f) void NetSessions::Insert(Connection* c) { - assert(c->Key()); + assert(c->IsKeyValid()); - Connection* old = 0; + Connection* old = nullptr; switch ( c->ConnTransport() ) { - // Remove first. Otherwise the dictioanry would still - // reference the old key for already existing connections. + // Remove first. Otherwise the map would still reference the old key for + // already existing connections. case TRANSPORT_TCP: - old = (Connection*) tcp_conns.Remove(c->Key()); - tcp_conns.Insert(c->Key(), c); + old = LookupConn(tcp_conns, c->Key()); + tcp_conns.erase(c->Key()); + InsertConnection(&tcp_conns, c->Key(), c); break; case TRANSPORT_UDP: - old = (Connection*) udp_conns.Remove(c->Key()); - udp_conns.Insert(c->Key(), c); + old = LookupConn(udp_conns, c->Key()); + udp_conns.erase(c->Key()); + InsertConnection(&udp_conns, c->Key(), c); break; case TRANSPORT_ICMP: - old = (Connection*) icmp_conns.Remove(c->Key()); - icmp_conns.Insert(c->Key(), c); + old = LookupConn(icmp_conns, c->Key()); + icmp_conns.erase(c->Key()); + InsertConnection(&icmp_conns, c->Key(), c); break; default: @@ -1151,7 +1149,6 @@ void NetSessions::Insert(Connection* c) // Some clean-ups similar to those in Remove() (but invisible // to the script layer). old->CancelTimers(); - delete old->Key(); old->ClearKey(); Unref(old); } @@ -1159,29 +1156,23 @@ void NetSessions::Insert(Connection* c) void NetSessions::Drain() { - IterCookie* cookie = tcp_conns.InitForIteration(); - Connection* tc; - - while ( (tc = tcp_conns.NextEntry(cookie)) ) + for ( const auto& entry : tcp_conns ) { + Connection* tc = entry.second; tc->Done(); tc->Event(connection_state_remove, 0); } - cookie = udp_conns.InitForIteration(); - Connection* uc; - - while ( (uc = udp_conns.NextEntry(cookie)) ) + for ( const auto& entry : udp_conns ) { + Connection* uc = entry.second; uc->Done(); uc->Event(connection_state_remove, 0); } - cookie = icmp_conns.InitForIteration(); - Connection* ic; - - while ( (ic = icmp_conns.NextEntry(cookie)) ) + for ( const auto& entry : icmp_conns ) { + Connection* ic = entry.second; ic->Done(); ic->Event(connection_state_remove, 0); } @@ -1191,22 +1182,22 @@ void NetSessions::Drain() void NetSessions::GetStats(SessionStats& s) const { - s.num_TCP_conns = tcp_conns.Length(); - s.cumulative_TCP_conns = tcp_conns.NumCumulativeInserts(); - s.num_UDP_conns = udp_conns.Length(); - s.cumulative_UDP_conns = udp_conns.NumCumulativeInserts(); - s.num_ICMP_conns = icmp_conns.Length(); - s.cumulative_ICMP_conns = icmp_conns.NumCumulativeInserts(); + s.num_TCP_conns = tcp_conns.size(); + s.cumulative_TCP_conns = stats.cumulative_TCP_conns; + s.num_UDP_conns = udp_conns.size(); + s.cumulative_UDP_conns = stats.cumulative_UDP_conns; + s.num_ICMP_conns = icmp_conns.size(); + s.cumulative_ICMP_conns = stats.cumulative_ICMP_conns; s.num_fragments = fragments.Length(); s.num_packets = num_packets_processed; - s.max_TCP_conns = tcp_conns.MaxLength(); - s.max_UDP_conns = udp_conns.MaxLength(); - s.max_ICMP_conns = icmp_conns.MaxLength(); + s.max_TCP_conns = stats.max_TCP_conns; + s.max_UDP_conns = stats.max_UDP_conns; + s.max_ICMP_conns = stats.max_ICMP_conns; s.max_fragments = fragments.MaxLength(); } -Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id, +Connection* NetSessions::NewConn(const ConnIDKey& k, double t, const ConnID* id, const u_char* data, int proto, uint32_t flow_label, const Packet* pkt, const EncapsulationStack* encapsulation) { @@ -1282,6 +1273,15 @@ Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id, return conn; } +Connection* NetSessions::LookupConn(const ConnectionMap& conns, const ConnIDKey& key) + { + auto it = conns.find(key); + if ( it != conns.end() ) + return it->second; + + return nullptr; + } + bool NetSessions::IsLikelyServerPort(uint32_t port, TransportProto proto) const { // We keep a cached in-core version of the table to speed up the lookup. @@ -1441,23 +1441,14 @@ unsigned int NetSessions::ConnectionMemoryUsage() // Connections have been flushed already. return 0; - IterCookie* cookie = tcp_conns.InitForIteration(); - Connection* tc; + for ( const auto& entry : tcp_conns ) + mem += entry.second->MemoryAllocation(); - while ( (tc = tcp_conns.NextEntry(cookie)) ) - mem += tc->MemoryAllocation(); + for ( const auto& entry : udp_conns ) + mem += entry.second->MemoryAllocation(); - cookie = udp_conns.InitForIteration(); - Connection* uc; - - while ( (uc = udp_conns.NextEntry(cookie)) ) - mem += uc->MemoryAllocation(); - - cookie = icmp_conns.InitForIteration(); - Connection* ic; - - while ( (ic = icmp_conns.NextEntry(cookie)) ) - mem += ic->MemoryAllocation(); + for ( const auto& entry : icmp_conns ) + mem += entry.second->MemoryAllocation(); return mem; } @@ -1470,23 +1461,14 @@ unsigned int NetSessions::ConnectionMemoryUsageConnVals() // Connections have been flushed already. return 0; - IterCookie* cookie = tcp_conns.InitForIteration(); - Connection* tc; + for ( const auto& entry : tcp_conns ) + mem += entry.second->MemoryAllocationConnVal(); - while ( (tc = tcp_conns.NextEntry(cookie)) ) - mem += tc->MemoryAllocationConnVal(); + for ( const auto& entry : udp_conns ) + mem += entry.second->MemoryAllocationConnVal(); - cookie = udp_conns.InitForIteration(); - Connection* uc; - - while ( (uc = udp_conns.NextEntry(cookie)) ) - mem += uc->MemoryAllocationConnVal(); - - cookie = icmp_conns.InitForIteration(); - Connection* ic; - - while ( (ic = icmp_conns.NextEntry(cookie)) ) - mem += ic->MemoryAllocationConnVal(); + for ( const auto& entry : icmp_conns ) + mem += entry.second->MemoryAllocationConnVal(); return mem; } @@ -1500,16 +1482,35 @@ unsigned int NetSessions::MemoryAllocation() return ConnectionMemoryUsage() + padded_sizeof(*this) + ch->MemoryAllocation() - // must take care we don't count the HaskKeys twice. - + tcp_conns.MemoryAllocation() - padded_sizeof(tcp_conns) - - // 12 is sizeof(Key) from ConnID::BuildConnKey(); - // it can't be (easily) accessed here. :-( - (tcp_conns.Length() * pad_size(12)) - + udp_conns.MemoryAllocation() - padded_sizeof(udp_conns) - - (udp_conns.Length() * pad_size(12)) - + icmp_conns.MemoryAllocation() - padded_sizeof(icmp_conns) - - (icmp_conns.Length() * pad_size(12)) + + padded_sizeof(tcp_conns.size() * (sizeof(ConnectionMap::key_type) + sizeof(ConnectionMap::value_type))) + + padded_sizeof(udp_conns.size() * (sizeof(ConnectionMap::key_type) + sizeof(ConnectionMap::value_type))) + + padded_sizeof(icmp_conns.size() * (sizeof(ConnectionMap::key_type) + sizeof(ConnectionMap::value_type))) + fragments.MemoryAllocation() - padded_sizeof(fragments) // FIXME: MemoryAllocation() not implemented for rest. ; } + +void NetSessions::InsertConnection(ConnectionMap* m, const ConnIDKey& key, Connection* conn) + { + (*m)[key] = conn; + + switch ( conn->ConnTransport() ) + { + case TRANSPORT_TCP: + stats.cumulative_TCP_conns++; + if ( m->size() > stats.max_TCP_conns ) + stats.max_TCP_conns = m->size(); + break; + case TRANSPORT_UDP: + stats.cumulative_UDP_conns++; + if ( m->size() > stats.max_UDP_conns ) + stats.max_UDP_conns = m->size(); + break; + case TRANSPORT_ICMP: + stats.cumulative_ICMP_conns++; + if ( m->size() > stats.max_ICMP_conns ) + stats.max_ICMP_conns = m->size(); + break; + default: break; + } + } diff --git a/src/Sessions.h b/src/Sessions.h index da069e218f..20ee491ff3 100644 --- a/src/Sessions.h +++ b/src/Sessions.h @@ -3,6 +3,9 @@ #ifndef sessions_h #define sessions_h +#include +#include + #include "Dict.h" #include "CompHash.h" #include "IP.h" @@ -13,8 +16,6 @@ #include "TunnelEncapsulation.h" #include "analyzer/protocol/tcp/Stats.h" -#include - class EncapsulationStack; class Connection; class ConnCompressor; @@ -27,20 +28,20 @@ namespace analyzer { namespace stepping_stone { class SteppingStoneManager; } } namespace analyzer { namespace arp { class ARP_Analyzer; } } struct SessionStats { - int num_TCP_conns; - int max_TCP_conns; + size_t num_TCP_conns; + size_t max_TCP_conns; uint64_t cumulative_TCP_conns; - int num_UDP_conns; - int max_UDP_conns; + size_t num_UDP_conns; + size_t max_UDP_conns; uint64_t cumulative_UDP_conns; - int num_ICMP_conns; - int max_ICMP_conns; + size_t num_ICMP_conns; + size_t max_ICMP_conns; uint64_t cumulative_ICMP_conns; - int num_fragments; - int max_fragments; + size_t num_fragments; + size_t max_fragments; uint64_t num_packets; }; @@ -112,8 +113,7 @@ public: unsigned int CurrentConnections() { - return tcp_conns.Length() + udp_conns.Length() + - icmp_conns.Length(); + return tcp_conns.size() + udp_conns.size() + icmp_conns.size(); } void DoNextPacket(double t, const Packet *pkt, const IP_Hdr* ip_hdr, @@ -172,10 +172,14 @@ protected: friend class TimerMgrExpireTimer; friend class IPTunnelTimer; - Connection* NewConn(HashKey* k, double t, const ConnID* id, + using ConnectionMap = std::map; + + Connection* NewConn(const ConnIDKey& k, double t, const ConnID* id, const u_char* data, int proto, uint32_t flow_label, const Packet* pkt, const EncapsulationStack* encapsulation); + Connection* LookupConn(const ConnectionMap& conns, const ConnIDKey& key); + // Check whether the tag of the current packet is consistent with // the given connection. Returns: // -1 if current packet is to be completely ignored. @@ -212,12 +216,19 @@ protected: bool CheckHeaderTrunc(int proto, uint32_t len, uint32_t caplen, const Packet *pkt, const EncapsulationStack* encap); + // Inserts a new connection into the sessions map. If a connection with + // the same key already exists in the map, it will be overwritten by + // the new one. + void InsertConnection(ConnectionMap* m, const ConnIDKey& key, Connection* conn); + CompositeHash* ch; - PDict tcp_conns; - PDict udp_conns; - PDict icmp_conns; + ConnectionMap tcp_conns; + ConnectionMap udp_conns; + ConnectionMap icmp_conns; PDict fragments; + SessionStats stats; + typedef pair IPPair; typedef pair TunnelActivity; typedef std::map IPTunnelMap; diff --git a/src/Stats.cc b/src/Stats.cc index 5b23f89035..f586435b6a 100644 --- a/src/Stats.cc +++ b/src/Stats.cc @@ -132,7 +132,7 @@ void ProfileLogger::Log() SessionStats s; sessions->GetStats(s); - file->Write(fmt("%.06f Conns: tcp=%d/%d udp=%d/%d icmp=%d/%d\n", + file->Write(fmt("%.06f Conns: tcp=%lu/%lu udp=%lu/%lu icmp=%lu/%lu\n", network_time, s.num_TCP_conns, s.max_TCP_conns, s.num_UDP_conns, s.max_UDP_conns,