diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4cb73448f..9f641baa65 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -286,6 +286,7 @@ set(MAIN_SRCS Scope.cc ScriptCoverageManager.cc SerializationFormat.cc + Session.cc Sessions.cc SmithWaterman.cc Stats.cc diff --git a/src/Conn.cc b/src/Conn.cc index ef5064b8e6..1755c9150c 100644 --- a/src/Conn.cc +++ b/src/Conn.cc @@ -21,53 +21,18 @@ #include "zeek/iosource/IOSource.h" namespace zeek { -namespace detail { - -void ConnectionTimer::Init(Connection* arg_conn, timer_func arg_timer, - bool arg_do_expire) - { - conn = arg_conn; - timer = arg_timer; - do_expire = arg_do_expire; - Ref(conn); - } - -ConnectionTimer::~ConnectionTimer() - { - if ( conn->RefCnt() < 1 ) - reporter->InternalError("reference count inconsistency in ~ConnectionTimer"); - - conn->RemoveTimer(this); - Unref(conn); - } - -void ConnectionTimer::Dispatch(double t, bool is_expire) - { - if ( is_expire && ! do_expire ) - return; - - // Remove ourselves from the connection's set of timers so - // it doesn't try to cancel us. - conn->RemoveTimer(this); - - (conn->*timer)(t); - - if ( conn->RefCnt() < 1 ) - reporter->InternalError("reference count inconsistency in ConnectionTimer::Dispatch"); - } - -} // namespace detail uint64_t Connection::total_connections = 0; uint64_t Connection::current_connections = 0; Connection::Connection(NetSessions* s, const detail::ConnIDKey& k, double t, const ConnID* id, uint32_t flow, const Packet* pkt) + : Session(t, connection_timeout, connection_status_update, + detail::connection_status_update_interval) { sessions = s; key = k; key_valid = true; - start_time = last_time = t; orig_addr = id->src_addr; resp_addr = id->dst_addr; @@ -92,19 +57,11 @@ Connection::Connection(NetSessions* s, const detail::ConnIDKey& k, double t, vlan = pkt->vlan; inner_vlan = pkt->inner_vlan; - is_active = 1; skip = 0; weird = 0; suppress_event = 0; - record_contents = record_packets = 1; - record_current_packet = record_current_content = 0; - - timers_canceled = 0; - inactivity_timeout = 0; - installed_status_timer = 0; - finished = 0; hist_seen = 0; @@ -217,12 +174,6 @@ void Connection::NextPacket(double t, bool is_orig, run_state::current_pkt = nullptr; } -void Connection::SetLifetime(double lifetime) - { - ADD_TIMER(&Connection::DeleteTimer, run_state::network_time + lifetime, 0, - detail::TIMER_CONN_DELETE); - } - bool Connection::IsReuse(double t, const u_char* pkt) { return root_analyzer && root_analyzer->IsReuse(t, pkt); @@ -271,76 +222,6 @@ void Connection::HistoryThresholdEvent(EventHandlerPtr e, bool is_orig, ); } -void Connection::DeleteTimer(double /* t */) - { - if ( is_active ) - Event(connection_timeout, nullptr); - - sessions->Remove(this); - } - -void Connection::InactivityTimer(double t) - { - if ( last_time + inactivity_timeout <= t ) - { - Event(connection_timeout, nullptr); - sessions->Remove(this); - ++detail::killed_by_inactivity; - } - else - ADD_TIMER(&Connection::InactivityTimer, - last_time + inactivity_timeout, 0, - detail::TIMER_CONN_INACTIVITY); - } - -void Connection::RemoveConnectionTimer(double t) - { - RemovalEvent(); - sessions->Remove(this); - } - -void Connection::SetInactivityTimeout(double timeout) - { - if ( timeout == inactivity_timeout ) - return; - - // First cancel and remove any existing inactivity timer. - for ( const auto& timer : timers ) - if ( timer->Type() == detail::TIMER_CONN_INACTIVITY ) - { - detail::timer_mgr->Cancel(timer); - break; - } - - if ( timeout ) - ADD_TIMER(&Connection::InactivityTimer, - last_time + timeout, 0, detail::TIMER_CONN_INACTIVITY); - - inactivity_timeout = timeout; - } - -void Connection::EnableStatusUpdateTimer() - { - if ( installed_status_timer ) - return; - - if ( connection_status_update && zeek::detail::connection_status_update_interval ) - { - ADD_TIMER(&Connection::StatusUpdateTimer, - run_state::network_time + detail::connection_status_update_interval, 0, - detail::TIMER_CONN_STATUS_UPDATE); - installed_status_timer = 1; - } - } - -void Connection::StatusUpdateTimer(double t) - { - EnqueueEvent(connection_status_update, nullptr, ConnVal()); - ADD_TIMER(&Connection::StatusUpdateTimer, - run_state::network_time + detail::connection_status_update_interval, 0, - detail::TIMER_CONN_STATUS_UPDATE); - } - const RecordValPtr& Connection::ConnVal() { if ( ! conn_val ) @@ -460,68 +341,12 @@ void Connection::RemovalEvent() EnqueueEvent(connection_state_remove, nullptr, ConnVal()); } -void Connection::Event(EventHandlerPtr f, analyzer::Analyzer* analyzer, const char* name) - { - if ( ! f ) - return; - - if ( name ) - EnqueueEvent(f, analyzer, make_intrusive(name), ConnVal()); - else - EnqueueEvent(f, analyzer, ConnVal()); - } - -void Connection::EnqueueEvent(EventHandlerPtr f, analyzer::Analyzer* a, - Args args) - { - // "this" is passed as a cookie for the event - event_mgr.Enqueue(f, std::move(args), util::detail::SOURCE_LOCAL, a ? a->GetID() : 0, this); - } - void Connection::Weird(const char* name, const char* addl, const char* source) { weird = 1; reporter->Weird(this, name, addl ? addl : "", source ? source : ""); } -void Connection::AddTimer(timer_func timer, double t, bool do_expire, - detail::TimerType type) - { - if ( timers_canceled ) - return; - - // 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_valid ) - return; - - detail::Timer* conn_timer = new detail::ConnectionTimer(this, timer, t, do_expire, type); - detail::timer_mgr->Add(conn_timer); - timers.push_back(conn_timer); - } - -void Connection::RemoveTimer(detail::Timer* t) - { - timers.remove(t); - } - -void Connection::CancelTimers() - { - // We are going to cancel our timers which, in turn, may cause them to - // call RemoveTimer(), which would then modify the list we're just - // traversing. Thus, we first make a copy of the list which we then - // iterate through. - TimerPList tmp(timers.length()); - std::copy(timers.begin(), timers.end(), std::back_inserter(tmp)); - - for ( const auto& timer : tmp ) - detail::timer_mgr->Cancel(timer); - - timers_canceled = 1; - timers.clear(); - } - void Connection::FlipRoles() { IPAddr tmp_addr = resp_addr; @@ -558,7 +383,7 @@ void Connection::FlipRoles() unsigned int Connection::MemoryAllocation() const { - return padded_sizeof(*this) + return Session::MemoryAllocation() + padded_sizeof(*this) + (timers.MemoryAllocation() - padded_sizeof(timers)) + (conn_val ? conn_val->MemoryAllocation() : 0) + (root_analyzer ? root_analyzer->MemoryAllocation(): 0) @@ -573,10 +398,7 @@ unsigned int Connection::MemoryAllocationConnVal() const void Connection::Describe(ODesc* d) const { - d->Add(start_time); - d->Add("("); - d->Add(last_time); - d->AddSP(")"); + Session::Describe(d); switch ( proto ) { case TRANSPORT_TCP: diff --git a/src/Conn.h b/src/Conn.h index 4f56563962..6b0aab34c1 100644 --- a/src/Conn.h +++ b/src/Conn.h @@ -16,6 +16,7 @@ #include "zeek/WeirdState.h" #include "zeek/ZeekArgs.h" #include "zeek/IntrusivePtr.h" +#include "zeek/Session.h" #include "zeek/iosource/Packet.h" #include "zeek/analyzer/Tag.h" @@ -34,7 +35,6 @@ using RecordValPtr = IntrusivePtr; namespace detail { -class ConnectionTimer; class Specific_RE_Matcher; class RuleEndpointState; class RuleHdrTest; @@ -55,8 +55,6 @@ enum ConnEventToFlag { NUM_EVENTS_TO_FLAG, }; -typedef void (Connection::*timer_func)(double t); - struct ConnID { IPAddr src_addr; IPAddr dst_addr; @@ -71,25 +69,29 @@ static inline int addr_port_canon_lt(const IPAddr& addr1, uint32_t p1, return addr1 < addr2 || (addr1 == addr2 && p1 < p2); } -class Connection final : public Obj { +class Connection final : public Session { public: Connection(NetSessions* s, const detail::ConnIDKey& k, double t, const ConnID* id, uint32_t flow, const Packet* pkt); ~Connection() override; - // Invoked when an encapsulation is discovered. It records the - // encapsulation with the connection and raises a "tunnel_changed" - // event if it's different from the previous encapsulation (or the - // first encountered). encap can be null to indicate no - // encapsulation. + /** + * Invoked when an encapsulation is discovered. It records the encapsulation + * with the connection and raises a "tunnel_changed" event if it's different + * from the previous encapsulation or if it's the first one encountered. + * + * @param encap The new encapsulation. Can be set to null to indicated no + * encapsulation or clear an old one. + */ void CheckEncapsulation(const std::shared_ptr& encap); - // Invoked when connection is about to be removed. Use Ref(this) - // inside Done to keep the connection object around (though it'll - // no longer be accessible from the dictionary of active - // connections). - void Done(); + /** + * Invoked when the session is about to be removed. Use Ref(this) + * inside Done to keep the session object around, though it'll + * no longer be accessible from the SessionManager. + */ + void Done() override; // Process the connection's next packet. "data" points just // beyond the IP header. It's updated to point just beyond @@ -111,13 +113,10 @@ public: // connection is in the session map. If it is removed, the key // should be marked invalid. const detail::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; } - double LastTime() const { return last_time; } - void SetLastTime(double t) { last_time = t; } + detail::SessionKey SessionKey(bool copy) const override + { return detail::SessionKey{&key, sizeof(key), copy}; } + void ClearKey() override { key_valid = false; } + bool IsKeyValid() const override { return key_valid; } const IPAddr& OrigAddr() const { return orig_addr; } const IPAddr& RespAddr() const { return resp_addr; } @@ -133,23 +132,6 @@ public: TransportProto ConnTransport() const { return proto; } - // True if we should record subsequent packets (either headers or - // in their entirety, depending on record_contents). We still - // record subsequent SYN/FIN/RST, regardless of how this is set. - bool RecordPackets() const { return record_packets; } - void SetRecordPackets(bool do_record) { record_packets = do_record ? 1 : 0; } - - // True if we should record full packets for this connection, - // false if we should just record headers. - bool RecordContents() const { return record_contents; } - void SetRecordContents(bool do_record) { record_contents = do_record ? 1 : 0; } - - // Set whether to record *current* packet header/full. - void SetRecordCurrentPacket(bool do_record) - { record_current_packet = do_record ? 1 : 0; } - void SetRecordCurrentContent(bool do_record) - { record_current_content = do_record ? 1 : 0; } - // FIXME: Now this is in Analyzer and should eventually be removed here. // // If true, skip processing of remainder of connection. Note @@ -158,26 +140,19 @@ public: void SetSkip(bool do_skip) { skip = do_skip ? 1 : 0; } bool Skipping() const { return skip; } - // Arrange for the connection to expire after the given amount of time. - void SetLifetime(double lifetime); - // Returns true if the packet reflects a reuse of this // connection (i.e., not a continuation but the beginning of // a new connection). bool IsReuse(double t, const u_char* pkt); - // Get/set the inactivity timeout for this connection. - void SetInactivityTimeout(double timeout); - double InactivityTimeout() const { return inactivity_timeout; } - - // Activate connection_status_update timer. - void EnableStatusUpdateTimer(); - /** * Returns the associated "connection" record. */ - const RecordValPtr& ConnVal(); + const RecordValPtr& ConnVal() override; + /** + * Append additional entries to the history field in the connection record. + */ void AppendAddl(const char* str); void Match(detail::Rule::PatternType type, const u_char* data, int len, @@ -186,36 +161,11 @@ public: /** * Generates connection removal event(s). */ - void RemovalEvent(); - - // If a handler exists for 'f', an event will be generated. If 'name' is - // given that event's first argument will be it, and it's second will be - // the connection value. If 'name' is null, then the event's first - // argument is the connection value. - void Event(EventHandlerPtr f, analyzer::Analyzer* analyzer, const char* name = nullptr); - - /** - * Enqueues an event associated with this connection and given analyzer. - */ - void EnqueueEvent(EventHandlerPtr f, analyzer::Analyzer* analyzer, - Args args); - - /** - * A version of EnqueueEvent() taking a variable number of arguments. - */ - template - std::enable_if_t< - std::is_convertible_v< - std::tuple_element_t<0, std::tuple>, ValPtr>> - EnqueueEvent(EventHandlerPtr h, analyzer::Analyzer* analyzer, Args&&... args) - { return EnqueueEvent(h, analyzer, zeek::Args{std::forward(args)...}); } + void RemovalEvent() override; void Weird(const char* name, const char* addl = "", const char* source = ""); bool DidWeird() const { return weird != 0; } - // Cancel all associated timers. - void CancelTimers(); - inline bool FlagEvent(ConnEventToFlag e) { if ( e >= 0 && e < NUM_EVENTS_TO_FLAG ) @@ -234,8 +184,8 @@ public: // Statistics. // Just a lower bound. - unsigned int MemoryAllocation() const; - unsigned int MemoryAllocationConnVal() const; + unsigned int MemoryAllocation() const override; + unsigned int MemoryAllocationConnVal() const override; static uint64_t TotalConnections() { return total_connections; } @@ -268,8 +218,6 @@ public: void AddHistory(char code) { history += code; } - void DeleteTimer(double t); - // Sets the root of the analyzer tree as well as the primary PIA. void SetRootAnalyzer(analyzer::TransportLayerAnalyzer* analyzer, analyzer::pia::PIA* pia); analyzer::TransportLayerAnalyzer* GetRootAnalyzer() { return root_analyzer; } @@ -295,26 +243,10 @@ public: protected: - // Add the given timer to expire at time t. If do_expire - // is true, then the timer is also evaluated when Bro terminates, - // otherwise not. - void AddTimer(timer_func timer, double t, bool do_expire, - detail::TimerType type); - - void RemoveTimer(detail::Timer* t); - // Allow other classes to access pointers to these: - friend class detail::ConnectionTimer; - - void InactivityTimer(double t); - void StatusUpdateTimer(double t); - void RemoveConnectionTimer(double t); + friend class detail::SessionTimer; NetSessions* sessions; - detail::ConnIDKey key; - bool key_valid; - - TimerPList timers; IPAddr orig_addr; IPAddr resp_addr; @@ -324,59 +256,30 @@ protected: uint32_t vlan, inner_vlan; // VLAN this connection traverses, if available u_char orig_l2_addr[Packet::L2_ADDR_LEN]; // Link-layer originator address, if available u_char resp_l2_addr[Packet::L2_ADDR_LEN]; // Link-layer responder address, if available - double start_time, last_time; - double inactivity_timeout; + int suppress_event; // suppress certain events to once per conn. RecordValPtr conn_val; std::shared_ptr encapsulation; // tunnels - int suppress_event; // suppress certain events to once per conn. - unsigned int installed_status_timer:1; - unsigned int timers_canceled:1; - unsigned int is_active:1; + detail::ConnIDKey key; + bool key_valid; + unsigned int skip:1; unsigned int weird:1; unsigned int finished:1; - unsigned int record_packets:1, record_contents:1; - unsigned int record_current_packet:1, record_current_content:1; unsigned int saw_first_orig_packet:1, saw_first_resp_packet:1; - // Count number of connections. - static uint64_t total_connections; - static uint64_t current_connections; - - std::string history; uint32_t hist_seen; + std::string history; analyzer::TransportLayerAnalyzer* root_analyzer; analyzer::pia::PIA* primary_PIA; UID uid; // Globally unique connection ID. detail::WeirdStateMap weird_state; + + // Count number of connections. + static uint64_t total_connections; + static uint64_t current_connections; }; -namespace detail { - -class ConnectionTimer final : public Timer { -public: - ConnectionTimer(Connection* arg_conn, timer_func arg_timer, - double arg_t, bool arg_do_expire, TimerType arg_type) - : Timer(arg_t, arg_type) - { Init(arg_conn, arg_timer, arg_do_expire); } - ~ConnectionTimer() override; - - void Dispatch(double t, bool is_expire) override; - -protected: - - void Init(Connection* conn, timer_func timer, bool do_expire); - - Connection* conn; - timer_func timer; - bool do_expire; -}; - -} // namespace detail } // namespace zeek - -#define ADD_TIMER(timer, t, do_expire, type) \ - AddTimer(timer_func(timer), (t), (do_expire), (type)) diff --git a/src/Session.cc b/src/Session.cc new file mode 100644 index 0000000000..307877ded6 --- /dev/null +++ b/src/Session.cc @@ -0,0 +1,273 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +#include "zeek/Session.h" + +#include "zeek/Reporter.h" +#include "zeek/analyzer/Analyzer.h" +#include "zeek/Val.h" +#include "zeek/Event.h" +#include "zeek/Desc.h" +#include "zeek/Sessions.h" +#include "zeek/IP.h" + +namespace zeek { + +namespace detail { + +void SessionTimer::Init(Session* arg_conn, timer_func arg_timer, + bool arg_do_expire) + { + conn = arg_conn; + timer = arg_timer; + do_expire = arg_do_expire; + Ref(conn); + } + +SessionTimer::~SessionTimer() + { + if ( conn->RefCnt() < 1 ) + reporter->InternalError("reference count inconsistency in ~SessionTimer"); + + conn->RemoveTimer(this); + Unref(conn); + } + +void SessionTimer::Dispatch(double t, bool is_expire) + { + if ( is_expire && ! do_expire ) + return; + + // Remove ourselves from the connection's set of timers so + // it doesn't try to cancel us. + conn->RemoveTimer(this); + + (conn->*timer)(t); + + if ( conn->RefCnt() < 1 ) + reporter->InternalError("reference count inconsistency in SessionTimer::Dispatch"); + } + +SessionKey::SessionKey(const void* session, size_t size, bool copy) : size(size) + { + data = reinterpret_cast(session); + if ( copy ) + CopyData(); + } + +SessionKey::SessionKey(SessionKey&& rhs) + { + data = rhs.data; + size = rhs.size; + copied = rhs.copied; + + rhs.data = nullptr; + rhs.size = 0; + rhs.copied = false; + } + +SessionKey& SessionKey::operator=(SessionKey&& rhs) + { + if ( this != &rhs ) + { + data = rhs.data; + size = rhs.size; + copied = rhs.copied; + + rhs.data = nullptr; + rhs.size = 0; + rhs.copied = false; + } + + return *this; + } + +SessionKey::~SessionKey() + { + if ( copied ) + delete [] data; + } + +void SessionKey::CopyData() + { + if ( copied ) + return; + + copied = true; + + uint8_t *temp = new uint8_t[size]; + memcpy(temp, data, size); + data = temp; + } + +bool SessionKey::operator<(const SessionKey& rhs) const + { + if ( size != rhs.size ) + return size < rhs.size; + + return memcmp(data, rhs.data, size) < 0; + } + +} // namespace detail + +Session::Session(double t, + EventHandlerPtr timeout_event, + EventHandlerPtr status_update_event, + double status_update_interval) + : start_time(t), last_time(t), + session_timeout_event(timeout_event), + session_status_update_event(status_update_event), + session_status_update_interval(status_update_interval) + { + record_contents = record_packets = 1; + record_current_packet = record_current_content = 0; + is_active = 1; + timers_canceled = 0; + inactivity_timeout = 0; + installed_status_timer = 0; + } + +unsigned int Session::MemoryAllocation() const + { + return 0; + } + +void Session::Event(EventHandlerPtr f, analyzer::Analyzer* analyzer, const char* name) + { + if ( ! f ) + return; + + if ( name ) + EnqueueEvent(f, analyzer, make_intrusive(name), ConnVal()); + else + EnqueueEvent(f, analyzer, ConnVal()); + } + +void Session::EnqueueEvent(EventHandlerPtr f, analyzer::Analyzer* a, Args args) + { + // "this" is passed as a cookie for the event + event_mgr.Enqueue(f, std::move(args), util::detail::SOURCE_LOCAL, a ? a->GetID() : 0, this); + } + +void Session::Describe(ODesc* d) const + { + d->Add(start_time); + d->Add("("); + d->Add(last_time); + d->AddSP(")"); + } + +void Session::SetLifetime(double lifetime) + { + ADD_TIMER(&Session::DeleteTimer, run_state::network_time + lifetime, 0, + detail::TIMER_CONN_DELETE); + } + +void Session::SetInactivityTimeout(double timeout) + { + if ( timeout == inactivity_timeout ) + return; + + // First cancel and remove any existing inactivity timer. + for ( const auto& timer : timers ) + if ( timer->Type() == detail::TIMER_CONN_INACTIVITY ) + { + detail::timer_mgr->Cancel(timer); + break; + } + + if ( timeout ) + ADD_TIMER(&Session::InactivityTimer, + last_time + timeout, 0, detail::TIMER_CONN_INACTIVITY); + + inactivity_timeout = timeout; + } + +void Session::EnableStatusUpdateTimer() + { + if ( installed_status_timer ) + return; + + if ( session_status_update_event && session_status_update_interval ) + { + ADD_TIMER(&Session::StatusUpdateTimer, + run_state::network_time + session_status_update_interval, 0, + detail::TIMER_CONN_STATUS_UPDATE); + installed_status_timer = 1; + } + } + +void Session::CancelTimers() + { + // We are going to cancel our timers which, in turn, may cause them to + // call RemoveTimer(), which would then modify the list we're just + // traversing. Thus, we first make a copy of the list which we then + // iterate through. + TimerPList tmp(timers.length()); + std::copy(timers.begin(), timers.end(), std::back_inserter(tmp)); + + for ( const auto& timer : tmp ) + detail::timer_mgr->Cancel(timer); + + timers_canceled = 1; + timers.clear(); + } + +void Session::DeleteTimer(double /* t */) + { + if ( is_active ) + Event(session_timeout_event, nullptr); + + sessions->Remove(this); + } + +void Session::AddTimer(timer_func timer, double t, bool do_expire, + detail::TimerType type) + { + if ( timers_canceled ) + return; + + // If the key is cleared, the session isn't stored in the session table + // anymore and will soon be deleted. We're not installed new timers + // anymore then. + if ( ! IsKeyValid() ) + return; + + detail::Timer* conn_timer = new detail::SessionTimer(this, timer, t, do_expire, type); + detail::timer_mgr->Add(conn_timer); + timers.push_back(conn_timer); + } + +void Session::RemoveTimer(detail::Timer* t) + { + timers.remove(t); + } + +void Session::InactivityTimer(double t) + { + if ( last_time + inactivity_timeout <= t ) + { + Event(session_timeout_event, nullptr); + sessions->Remove(this); + ++detail::killed_by_inactivity; + } + else + ADD_TIMER(&Session::InactivityTimer, + last_time + inactivity_timeout, 0, + detail::TIMER_CONN_INACTIVITY); + } + +void Session::StatusUpdateTimer(double t) + { + EnqueueEvent(session_status_update_event, nullptr, ConnVal()); + ADD_TIMER(&Session::StatusUpdateTimer, + run_state::network_time + session_status_update_interval, 0, + detail::TIMER_CONN_STATUS_UPDATE); + } + +void Session::RemoveConnectionTimer(double t) + { + RemovalEvent(); + sessions->Remove(this); + } + +} // namespace zeek diff --git a/src/Session.h b/src/Session.h new file mode 100644 index 0000000000..dfb7b66f35 --- /dev/null +++ b/src/Session.h @@ -0,0 +1,287 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +#pragma once + +#include + +#include "zeek/Hash.h" +#include "zeek/Obj.h" +#include "zeek/EventHandler.h" +#include "zeek/Timer.h" + +namespace zeek { + +class RecordVal; +using RecordValPtr = IntrusivePtr; +class Session; + +namespace analyzer { class Analyzer; } +namespace detail { + class SessionTimer; + class SessionKey; +} + +typedef void (Session::*timer_func)(double t); + +class Session : public Obj { +public: + + /** + * Construct a new session. + * + * @param t The timestamp for the start and end of the session when it's initially + * created. The end time will be updated as later packets are processed. + * @param timeout_event The event that will be emitted when the session times out. + * @param status_update_event The event that will be emitted for session status + * updates. This can be set to null to disable status updates. This event also + * won't be emitted unless the EnableStatusUpdateTimer() method is called on the + * session, and the interval is set to a non-zero value. + * @param status_update_interval The interval in seconds for the status update + * event to be emitted. Setting this to zero disables the status update timer. + */ + Session(double t, EventHandlerPtr timeout_event, + EventHandlerPtr status_update_event = nullptr, + double status_update_interval = 0); + + virtual ~Session() {} + + /** + * Invoked when the session is about to be removed. Use Ref(this) + * inside Done to keep the session object around, though it'll + * no longer be accessible from the SessionManager. + */ + virtual void Done() = 0; + + /** + * Returns a key for the session. This is used as the key for storing + * the session in SessionManager. + * + * @param copy Flag to indicate that the key returned has a copy of the + * key data instead of just a pointer to it. + */ + virtual detail::SessionKey SessionKey(bool copy) const = 0; + + /** + * Set the key as invalid. + */ + virtual void ClearKey() = 0; + + /** + * Return whether the key is valid. + */ + virtual bool IsKeyValid() const = 0; + + double StartTime() const { return start_time; } + void SetStartTime(double t) { start_time = t; } + double LastTime() const { return last_time; } + void SetLastTime(double t) { last_time = t; } + + // True if we should record subsequent packets (either headers or + // in their entirety, depending on record_contents). We still + // record subsequent SYN/FIN/RST, regardless of how this is set. + bool RecordPackets() const { return record_packets; } + void SetRecordPackets(bool do_record) { record_packets = do_record ? 1 : 0; } + + // True if we should record full packets for this connection, + // false if we should just record headers. + bool RecordContents() const { return record_contents; } + void SetRecordContents(bool do_record) { record_contents = do_record ? 1 : 0; } + + // Set whether to record *current* packet header/full. + void SetRecordCurrentPacket(bool do_record) + { record_current_packet = do_record ? 1 : 0; } + void SetRecordCurrentContent(bool do_record) + { record_current_content = do_record ? 1 : 0; } + + /** + * Returns the associated "session" record. + * TODO: rename this to SessionVal(). This requires a swath of other changes. + */ + virtual const RecordValPtr& ConnVal() = 0; + + /** + * Return the memory allocation required by the session record. This requires at + * least one call to ConnVal() first in order to setup the record object. + */ + virtual unsigned int MemoryAllocationConnVal() const = 0; + + /** + * A lower-bound calculation of how much memory a session object is using. + */ + virtual unsigned int MemoryAllocation() const; + + /** + * Generates session removal event(s). Must be overridden by child classes to + * provide specific removal events. + */ + virtual void RemovalEvent() = 0; + + /** + * Generate an event for this session. + * + * @param f The handler for the event to be generated. If the handler doesn't + * exist, this method doesn't generate anything. + * @param analyzer + * @param name If given, this will be passed as the first argument to the + * handler, followed by the session value. If null, then the event's first + * argument is the session value. + */ + void Event(EventHandlerPtr f, analyzer::Analyzer* analyzer = nullptr, + const char* name = nullptr); + + /** + * Enqueues an event associated with this connection and given analyzer. + */ + void EnqueueEvent(EventHandlerPtr f, analyzer::Analyzer* analyzer, Args args); + + /** + * A version of EnqueueEvent() taking a variable number of arguments. + */ + template + std::enable_if_t< + std::is_convertible_v< + std::tuple_element_t<0, std::tuple>, ValPtr>> + EnqueueEvent(EventHandlerPtr h, analyzer::Analyzer* analyzer, Args&&... args) + { return EnqueueEvent(h, analyzer, zeek::Args{std::forward(args)...}); } + + virtual void Describe(ODesc* d) const override; + + /** + * Sets the session to expire after a given amount of time. + * + * @param lifetime The amount of time in seconds from the current network time. + */ + void SetLifetime(double lifetime); + + /** + * Sets the inactivity timeout for this session. + * + * @param timeout The number of seconds of inactivity allowed for this session + * before it times out. + */ + void SetInactivityTimeout(double timeout); + + /** + * Returns the inactivity timeout for the session. + */ + double InactivityTimeout() const { return inactivity_timeout; } + + /** + * Activates the timer for the status update event. + */ + void EnableStatusUpdateTimer(); + + /** + * Cancels all timers associated with this session. + */ + void CancelTimers(); + + /** + * Called when the lifetime of the session expires. Fires a timeout event and + * removes the session from the manager. + * TODO: This function has a terrible name considering there's an AddTimer() and + * a RemoveTimer() method in this class as well. + * + * @param t This argument is ignored. + */ + void DeleteTimer(double t); + +protected: + + friend class detail::SessionTimer; + + /** + * Add a given timer to expire at a specific time. + * + * @param timer A pointer to a method that will be called when the timer expires. + * @param t The time when the timer expires. This is an absolute time, not a time + * relative to the current network time. + * @param do_expire If set to true, the timer is also evaluated when Zeek + * terminates. + * @param type The type of timer being added. + */ + void AddTimer(timer_func timer, double t, bool do_expire, + detail::TimerType type); + + /** + * Remove a specific timer from firing. + */ + void RemoveTimer(detail::Timer* t); + + /** + * The handler method for inactivity timers. + */ + void InactivityTimer(double t); + + /** + * The handler method for status update timers. + */ + void StatusUpdateTimer(double t); + + // TODO: is this method used by anyone? + void RemoveConnectionTimer(double t); + + double start_time, last_time; + TimerPList timers; + double inactivity_timeout; + + EventHandlerPtr session_timeout_event; + EventHandlerPtr session_status_update_event; + double session_status_update_interval; + + unsigned int installed_status_timer:1; + unsigned int timers_canceled:1; + unsigned int is_active:1; + unsigned int record_packets:1, record_contents:1; + unsigned int record_current_packet:1, record_current_content:1; + +}; + +namespace detail { + +class SessionTimer final : public Timer { +public: + SessionTimer(Session* arg_conn, timer_func arg_timer, + double arg_t, bool arg_do_expire, TimerType arg_type) + : Timer(arg_t, arg_type) + { Init(arg_conn, arg_timer, arg_do_expire); } + ~SessionTimer() override; + + void Dispatch(double t, bool is_expire) override; + +protected: + + void Init(Session* conn, timer_func timer, bool do_expire); + + Session* conn; + timer_func timer; + bool do_expire; +}; + +class SessionKey final { +public: + SessionKey(const void* session, size_t size, bool copy=false); + ~SessionKey(); + + // Implement move semantics for SessionKey, since they're used as keys + // in a map and copying them would cause double-free issues. Adding this + // constructor and operator explicitly disables the equivalent copy + // operations. + SessionKey(SessionKey&& rhs); + SessionKey& operator=(SessionKey&& rhs); + + void CopyData(); + + bool operator<(const SessionKey& rhs) const; + +private: + const uint8_t* data = nullptr; + size_t size = 0; + bool copied = false; +}; + +} // namespace detail +} // namespace zeek + +#define ADD_TIMER(timer, t, do_expire, type) \ + AddTimer(timer_func(timer), (t), (do_expire), (type)) diff --git a/src/Sessions.cc b/src/Sessions.cc index 5cb8e8745d..1b4212399b 100644 --- a/src/Sessions.cc +++ b/src/Sessions.cc @@ -18,6 +18,7 @@ #include "zeek/NetVar.h" #include "zeek/Reporter.h" #include "zeek/RuleMatcher.h" +#include "zeek/Session.h" #include "zeek/TunnelEncapsulation.h" #include "zeek/analyzer/protocol/icmp/ICMP.h" @@ -363,8 +364,10 @@ Connection* NetSessions::FindConnection(Val* v) return conn; } -void NetSessions::Remove(Connection* c) +void NetSessions::Remove(Session* s) { + Connection* c = static_cast(s); + if ( c->IsKeyValid() ) { const detail::ConnIDKey& key = c->Key(); diff --git a/src/Sessions.h b/src/Sessions.h index 79908a99f3..fe0fe4071e 100644 --- a/src/Sessions.h +++ b/src/Sessions.h @@ -17,6 +17,7 @@ namespace detail { class PacketFilter; } class EncapsulationStack; class Packet; class Connection; +class Session; struct ConnID; struct SessionStats { @@ -49,7 +50,16 @@ public: // no such connection or the Val is ill-formed. Connection* FindConnection(Val* v); - void Remove(Connection* c); + /** + * Looks up the connection referred to by a given key. + * + * @param key The key for the connection to search for. + * @param proto The transport protocol for the connection. + * @return The connection, or nullptr if one doesn't exist. + */ + Connection* FindConnection(const detail::ConnIDKey& key, TransportProto proto); + + void Remove(Session* s); void Insert(Connection* c); // Generating connection_pending events for all connections diff --git a/src/analyzer/protocol/tcp/TCP.h b/src/analyzer/protocol/tcp/TCP.h index 13c87df7fc..7903175e20 100644 --- a/src/analyzer/protocol/tcp/TCP.h +++ b/src/analyzer/protocol/tcp/TCP.h @@ -143,7 +143,7 @@ protected: void CheckRecording(bool need_contents, TCP_Flags flags); void CheckPIA_FirstPacket(bool is_orig, const IP_Hdr* ip); - friend class detail::ConnectionTimer; + friend class detail::SessionTimer; void AttemptTimer(double t); void PartialCloseTimer(double t); void ExpireTimer(double t);