一个基于IP的包分析器重用的基类。这是默认的通过TCP, UDP和ICMP的分析器来减少大量的重复代码。
class IPBasedAnalyzer : public Analyzer
{
public:
~IPBasedAnalyzer() override;
// 分析包,首先被调用
bool AnalyzePacket(size_t len, const uint8_t* data, Packet* packet) override;
// 如果分析器确定实际上已经启动了一个新连接,而连接语句没有终止前一个连接,则返回true,即,新数据到达了前一个实例的分析器。
// 是否重用,仅限于TCP
virtual bool IsReuse(double t, const u_char* pkt) { return false; }
// 注册一个端口
bool RegisterAnalyzerForPort(const zeek::Tag& tag, uint32_t port);
// 取消注册端口
bool UnregisterAnalyzerForPort(const zeek::Tag& tag, uint32_t port);
void DumpPortDebug();
static void SetIgnoreChecksumsNets(TableValPtr t);
static TableValPtr GetIgnoreChecksumsNets();
protected:
IPBasedAnalyzer(const char* name, TransportProto proto, uint32_t mask,
bool report_unknown_protocols);
// 将报头解析为ConnTuple对象
virtual bool BuildConnTuple(size_t len, const uint8_t* data, Packet* packet,
ConnTuple& tuple) = 0;
//将连接插入后继续处理数据包会话管理器。这应该被所有的子类实现。
virtual void DeliverPacket(Connection* conn, double t, bool is_orig, int remaining, Packet* pkt)
{
}
//在看到连接的第一个包时,检查是否需要分析它(例如,我们可能不想看部分连接)
//如果是,我们是否应该互换发起者和响应器基于已知的端口等。
virtual bool WantConnection(uint16_t src_port, uint16_t dst_port, const u_char* data,
bool& flip_roles) const
{
flip_roles = false;
return true;
}
// 返回会话连接适配器
virtual SessionAdapter* MakeSessionAdapter(Connection* conn) = 0;
virtual analyzer::pia::PIA* MakePIA(Connection* conn) { return nullptr; }
/**
* Verifies that there is enough data in the packet to process the header
* length requested.
*
* @param min_hdr_len The minimum data in bytes that needs to exist.
* @param remaining The remaining number of bytes in the packet reported by
* previous analyzer.
* @param packet The packet being processed. This will be used to pull out the
* number of bytes the IP header says we have remaining.
*/
bool CheckHeaderTrunc(size_t min_hdr_len, size_t remaining, Packet* packet);
/**
* Returns true if the port corresponds to an application for which there
* is a Zeek analyzer (even if it might not be used by the present policy
* script) or if it's generally a likely server port.
*
* @param port The port number to check, in host order.
*/
bool IsLikelyServerPort(uint32_t port) const;
private:
// While this is storing session analyzer tags, we store it here since packet analyzers
// are persitent objects. We can't do this in the adapters because those get created
// and destroyed for each connection.
using tag_set = std::set<zeek::Tag>;
using analyzer_map_by_port = std::map<uint32_t, tag_set*>;
analyzer_map_by_port analyzers_by_port;
tag_set* LookupPort(uint32_t port, bool add_if_not_found);
/**
* Creates a new Connection object from data gleaned from the current packet.
*
* @param id A connection ID generated from the packet data. This should have been
* passed in from a child analyzer.
* @param key A connection ID key generated from the ID.
* @param pkt The packet associated with the new connection.
*/
zeek::Connection* NewConn(const ConnTuple* id, const detail::ConnKey& key, const Packet* pkt);
void BuildSessionAnalyzerTree(Connection* conn);
TransportProto transport;
uint32_t server_port_mask;
static TableValPtr ignore_checksums_nets_table;
};
此函数是传输层前的处理函数:
bool IPBasedAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* pkt)
{
// 建立key
ConnTuple tuple;
if ( ! BuildConnTuple(len, data, pkt, tuple) )
return false;
const std::shared_ptr<IP_Hdr>& ip_hdr = pkt->ip_hdr;
detail::ConnKey key(tuple);
Connection* conn = session_mgr->FindConnection(key);
if ( ! conn )
{
// 连接不存在,建立连接
conn = NewConn(&tuple, key, pkt);
// 插入到管理器中即添加到map中
if ( conn )
session_mgr->Insert(conn, false);
}
else
{
// 判断
if ( conn->IsReuse(run_state::processing_start_time, ip_hdr->Payload()) )
{
conn->Event(connection_reused, nullptr);
session_mgr->Remove(conn);
conn = NewConn(&tuple, key, pkt);
if ( conn )
session_mgr->Insert(conn, false);
}
else
{
conn->CheckEncapsulation(pkt->encap);
}
}
if ( ! conn )
return false;
// If we successfuly made a connection for this packet that means it'll eventually
// get logged, which means we can mark this packet as having been processed.
pkt->processed = true;
bool is_orig = (tuple.src_addr == conn->OrigAddr()) && (tuple.src_port == conn->OrigPort());
pkt->is_orig = is_orig;
// IPV6的
conn->CheckFlowLabel(is_orig, ip_hdr->FlowLabel());
zeek::ValPtr pkt_hdr_val;
// IPV6的
if ( ipv6_ext_headers && ip_hdr->NumHeaders() > 1 )
{
pkt_hdr_val = ip_hdr->ToPktHdrVal();
conn->EnqueueEvent(ipv6_ext_headers, nullptr, conn->GetVal(), pkt_hdr_val);
}
// 事件回调
if ( new_packet )
conn->EnqueueEvent(new_packet, nullptr, conn->GetVal(),
pkt_hdr_val ? std::move(pkt_hdr_val) : ip_hdr->ToPktHdrVal());
conn->SetRecordPackets(true);
conn->SetRecordContents(true);
const u_char* payload = pkt->ip_hdr->Payload();
run_state::current_timestamp = run_state::processing_start_time;
run_state::current_pkt = pkt;
// TODO: Does this actually mean anything?
if ( conn->GetSessionAdapter()->Skipping() )
return true;
// 分离到响应的子类处理
DeliverPacket(conn, run_state::processing_start_time, is_orig, len, pkt);
run_state::current_timestamp = 0;
run_state::current_pkt = nullptr;
// 重组包,不进行dump
if ( pkt->ip_hdr->Reassembled() )
pkt->dump_packet = false;
else if ( conn->RecordPackets() )
{
pkt->dump_packet = true;
// If we don't want the content, set the dump size to include just
// the header.
if ( ! conn->RecordContents() )
pkt->dump_size = payload - pkt->data;
}
return true;
}
UDP
bool UDPAnalyzer::BuildConnTuple(size_t len, const uint8_t* data, Packet* packet, ConnTuple& tuple)
{
uint32_t min_hdr_len = sizeof(struct udphdr);
if ( ! CheckHeaderTrunc(min_hdr_len, len, packet) )
return false;
tuple.src_addr = packet->ip_hdr->SrcAddr();
tuple.dst_addr = packet->ip_hdr->DstAddr();
const struct udphdr* up = (const struct udphdr*)packet->ip_hdr->Payload();
tuple.src_port = up->uh_sport;
tuple.dst_port = up->uh_dport;
tuple.is_one_way = false;
tuple.proto = TRANSPORT_UDP;
return true;
}
TCP
bool TCPAnalyzer::BuildConnTuple(size_t len, const uint8_t* data, Packet* packet, ConnTuple& tuple)
{
uint32_t min_hdr_len = sizeof(struct tcphdr);
if ( ! CheckHeaderTrunc(min_hdr_len, len, packet) )
return false;
tuple.src_addr = packet->ip_hdr->SrcAddr();
tuple.dst_addr = packet->ip_hdr->DstAddr();
data = packet->ip_hdr->Payload();
const struct tcphdr* tp = (const struct tcphdr*)data;
tuple.src_port = tp->th_sport;
tuple.dst_port = tp->th_dport;
tuple.is_one_way = false;
tuple.proto = TRANSPORT_TCP;
return true;
}
using SessionMap = std::unordered_map<detail::Key, Session*, detail::KeyHash>;
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;
}
zeek::Connection* IPBasedAnalyzer::NewConn(const ConnTuple* id, const detail::ConnKey& key,
const Packet* pkt)
{
int src_h = ntohs(id->src_port);
int dst_h = ntohs(id->dst_port);
bool flip = false;
// 检查是否需要分析此连接,并且检查是否反转服务器/客户端角色
if ( ! WantConnection(src_h, dst_h, pkt->ip_hdr->Payload(), flip) )
return nullptr;
// 构造Connection对象
Connection* conn = new Connection(key, run_state::processing_start_time, id,
pkt->ip_hdr->FlowLabel(), pkt);
// 设置传输层协议
conn->SetTransport(transport);
// 反转服务器/客户端角色
if ( flip )
conn->FlipRoles();
// 构建协议分析器
BuildSessionAnalyzerTree(conn);
if ( new_connection )
conn->Event(new_connection, nullptr);
return conn;
}
void IPBasedAnalyzer::BuildSessionAnalyzerTree(Connection* conn)
{
SessionAdapter* root = MakeSessionAdapter(conn);
analyzer::pia::PIA* pia = MakePIA(conn);
bool scheduled = analyzer_mgr->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 ( ! analyzers_by_port.empty() && ! zeek::detail::dpd_ignore_ports )
{
int resp_port = ntohs(conn->RespPort());
std::set<zeek::Tag>* ports = LookupPort(resp_port, false);
if ( ports )
{
for ( const auto& port : *ports )
{
analyzer::Analyzer* analyzer = analyzer_mgr->InstantiateAnalyzer(port, conn);
if ( ! analyzer )
continue;
root->AddChildAnalyzer(analyzer, false);
DBG_ANALYZER_ARGS(conn, "activated %s analyzer due to port %d",
analyzer_mgr->GetComponentName(port).c_str(), resp_port);
}
}
}
}
root->AddExtraAnalyzers(conn);
if ( pia )
root->AddChildAnalyzer(pia->AsAnalyzer());
conn->SetSessionAdapter(root, pia);
root->Init();
root->InitChildren();
PLUGIN_HOOK_VOID(HOOK_SETUP_ANALYZER_TREE, HookSetupAnalyzerTree(conn));
}
SessionAdapter* TCPAnalyzer::MakeSessionAdapter(Connection* conn)
{
auto* root = new TCPSessionAdapter(conn);
root->SetParent(this);
conn->EnableStatusUpdateTimer();
conn->SetInactivityTimeout(zeek::detail::udp_inactivity_timeout);
return root;
}
bool Manager::ApplyScheduledAnalyzers(Connection* conn, bool init,
packet_analysis::IP::SessionAdapter* parent)
{
if ( ! parent )
parent = conn->GetSessionAdapter();
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);
if ( scheduled_analyzer_applied )
conn->EnqueueEvent(scheduled_analyzer_applied, nullptr, conn->GetVal(), it->AsVal());
DBG_ANALYZER_ARGS(conn, "activated %s analyzer as scheduled",
analyzer_mgr->GetComponentName(*it).c_str());
}
return expected.size();
}
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);
}
}
class Session : public Obj
{
public:
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 must have a copy of the
* key data instead of just a pointer to it.
*/
virtual detail::Key SessionKey(bool copy) const = 0;
/**
* Set whether this session is in the session table.
*/
void SetInSessionTable(bool in_table) { in_session_table = in_table; }
/**
* Return whether this session is in the session table.
*/
bool IsInSessionTable() const { return in_session_table; }
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 session,
// 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.
*/
virtual const RecordValPtr& GetVal() = 0;
[[deprecated("Remove in v5.1. Use GetVal().")]] const RecordValPtr& ConnVal()
{
return GetVal();
}
/**
* Return the memory allocation required by the session record. This requires at
* least one call to Get() first in order to setup the record object.
*/
[[deprecated("Remove in v5.1. MemoryAllocation() is deprecated and will be removed. See "
"GHI-572.")]] virtual unsigned int
MemoryAllocationVal() const = 0;
[[deprecated("Remove in v5.1. Use MemoryAllocationVal().")]] unsigned int
MemoryAllocationConnVal() const
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
return MemoryAllocationVal();
#pragma GCC diagnostic pop
}
/**
* A lower-bound calculation of how much memory a session object is using.
*/
[[deprecated("Remove in v5.1. MemoryAllocation() is deprecated and will be removed. See "
"GHI-572.")]] 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 session and given analyzer.
*/
void EnqueueEvent(EventHandlerPtr f, analyzer::Analyzer* analyzer, Args args);
/**
* A version of EnqueueEvent() taking a variable number of arguments.
*/
template <class... Args>
std::enable_if_t<std::is_convertible_v<std::tuple_element_t<0, std::tuple<Args...>>, ValPtr>>
EnqueueEvent(EventHandlerPtr h, analyzer::Analyzer* analyzer, Args&&... args)
{
return EnqueueEvent(h, analyzer, zeek::Args{std::forward<Args>(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);
/**
* Returns a string representation of the transport protocol referenced by the
* session. This is used by SessionManager for statistics.
*/
virtual std::string TransportIdentifier() const = 0;
AnalyzerConfirmationState AnalyzerState(const zeek::Tag& tag) const;
void SetAnalyzerState(const zeek::Tag& tag, AnalyzerConfirmationState);
protected:
friend class detail::Timer;
/**
* 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, zeek::detail::TimerType type);
/**
* Remove a specific timer from firing.
*/
void RemoveTimer(zeek::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;
bool in_session_table;
std::map<zeek::Tag, AnalyzerConfirmationState> analyzer_confirmations;
};
class Connection final : public session::Session
{
public:
Connection(const detail::ConnKey& k, double t, const ConnTuple* 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 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<EncapsulationStack>& encap);
/**
* 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
// the transport header (or whatever should be saved, if we
// decide not to save the full packet contents).
//
// If record_packet is true, the packet should be recorded.
// If record_content is true, then its entire contents should
// be recorded, otherwise just up through the transport header.
// Both are assumed set to true when called.
void NextPacket(double t, bool is_orig, const IP_Hdr* ip, int len, int caplen,
const u_char*& data, int& record_packet, int& record_content,
// arguments for reproducing packets
const Packet* pkt);
// 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 detail::ConnKey& Key() const { return key; }
session::detail::Key SessionKey(bool copy) const override
{
return session::detail::Key{&key, sizeof(key), session::detail::Key::CONNECTION_KEY_TYPE,
copy};
}
const IPAddr& OrigAddr() const { return orig_addr; }
const IPAddr& RespAddr() const { return resp_addr; }
uint32_t OrigPort() const { return orig_port; }
uint32_t RespPort() const { return resp_port; }
void FlipRoles();
analyzer::Analyzer* FindAnalyzer(analyzer::ID id);
analyzer::Analyzer* FindAnalyzer(const zeek::Tag& tag); // find first in tree.
analyzer::Analyzer* FindAnalyzer(const char* name); // find first in tree.
TransportProto ConnTransport() const { return proto; }
std::string TransportIdentifier() const override
{
if ( proto == TRANSPORT_TCP )
return "tcp";
else if ( proto == TRANSPORT_UDP )
return "udp";
else if ( proto == TRANSPORT_ICMP )
return "icmp";
else
return "unknown";
}
// 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);
/**
* Returns the associated "connection" record.
*/
const RecordValPtr& GetVal() 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, bool is_orig, bool bol,
bool eol, bool clear_state);
/**
* Generates connection removal event(s).
*/
void RemovalEvent() override;
void Weird(const char* name, const char* addl = "", const char* source = "");
bool DidWeird() const { return weird != 0; }
inline bool FlagEvent(ConnEventToFlag e)
{
if ( e >= 0 && e < NUM_EVENTS_TO_FLAG )
{
if ( suppress_event & (1 << e) )
return false;
suppress_event |= 1 << e;
}
return true;
}
void Describe(ODesc* d) const override;
void IDString(ODesc* d) const;
// Statistics.
// Just a lower bound.
[[deprecated("Remove in v5.1. MemoryAllocation() is deprecated and will be removed. See "
"GHI-572.")]] unsigned int
MemoryAllocation() const override;
[[deprecated("Remove in v5.1. MemoryAllocation() is deprecated and will be removed. See "
"GHI-572.")]] unsigned int
MemoryAllocationVal() const override;
static uint64_t TotalConnections() { return total_connections; }
static uint64_t CurrentConnections() { return current_connections; }
// Returns true if the history was already seen, false otherwise.
bool CheckHistory(uint32_t mask, char code)
{
if ( (hist_seen & mask) == 0 )
{
hist_seen |= mask;
AddHistory(code);
return false;
}
else
return true;
}
// Increments the passed counter and adds it as a history
// code if it has crossed the next scaling threshold. Scaling
// is done in terms of powers of the third argument.
// Returns true if the threshold was crossed, false otherwise.
bool ScaledHistoryEntry(char code, uint32_t& counter, uint32_t& scaling_threshold,
uint32_t scaling_base = 10);
void HistoryThresholdEvent(EventHandlerPtr e, bool is_orig, uint32_t threshold);
void AddHistory(char code) { history += code; }
// Sets the root of the analyzer tree as well as the primary PIA.
void SetSessionAdapter(packet_analysis::IP::SessionAdapter* aa, analyzer::pia::PIA* pia);
packet_analysis::IP::SessionAdapter* GetSessionAdapter() { return adapter; }
analyzer::pia::PIA* GetPrimaryPIA() { return primary_PIA; }
// Sets the transport protocol in use.
void SetTransport(TransportProto arg_proto) { proto = arg_proto; }
void SetUID(const UID& arg_uid) { uid = arg_uid; }
UID GetUID() const { return uid; }
std::shared_ptr<EncapsulationStack> GetEncapsulation() const { return encapsulation; }
void CheckFlowLabel(bool is_orig, uint32_t flow_label);
uint32_t GetOrigFlowLabel() { return orig_flow_label; }
uint32_t GetRespFlowLabel() { return resp_flow_label; }
bool PermitWeird(const char* name, uint64_t threshold, uint64_t rate, double duration);
private:
friend class session::detail::Timer;
IPAddr orig_addr;
IPAddr resp_addr;
uint32_t orig_port, resp_port; // in network order
TransportProto proto;
uint32_t orig_flow_label, resp_flow_label; // most recent IPv6 flow labels
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
int suppress_event; // suppress certain events to once per conn.
RecordValPtr conn_val;
std::shared_ptr<EncapsulationStack> encapsulation; // tunnels
detail::ConnKey key;
unsigned int weird : 1;
unsigned int finished : 1;
unsigned int saw_first_orig_packet : 1, saw_first_resp_packet : 1;
uint32_t hist_seen;
std::string history;
packet_analysis::IP::SessionAdapter* adapter;
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;
};