mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 22:58:20 +00:00
1353 lines
32 KiB
C++
1353 lines
32 KiB
C++
// $Id: Sessions.cc 7075 2010-09-13 02:39:38Z vern $
|
|
//
|
|
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include "Net.h"
|
|
#include "Event.h"
|
|
#include "Timer.h"
|
|
#include "NetVar.h"
|
|
#include "Sessions.h"
|
|
#include "Reporter.h"
|
|
#include "OSFinger.h"
|
|
|
|
#include "ICMP.h"
|
|
#include "UDP.h"
|
|
|
|
#include "DNS-binpac.h"
|
|
#include "HTTP-binpac.h"
|
|
|
|
#include "SteppingStone.h"
|
|
#include "BackDoor.h"
|
|
#include "InterConn.h"
|
|
#include "Discard.h"
|
|
#include "RuleMatcher.h"
|
|
#include "ConnCompressor.h"
|
|
#include "DPM.h"
|
|
|
|
#include "PacketSort.h"
|
|
#include "TunnelHandler.h"
|
|
|
|
// These represent NetBIOS services on ephemeral ports. They're numbered
|
|
// so that we can use a single int to hold either an actual TCP/UDP server
|
|
// port or one of these.
|
|
enum NetBIOS_Service {
|
|
NETBIOS_SERVICE_START = 0x10000L, // larger than any port
|
|
NETBIOS_SERVICE_DCE_RPC,
|
|
};
|
|
|
|
NetSessions* sessions;
|
|
|
|
void TimerMgrExpireTimer::Dispatch(double t, int is_expire)
|
|
{
|
|
if ( mgr->LastAdvance() + timer_mgr_inactivity_timeout < timer_mgr->Time() )
|
|
{
|
|
// Expired.
|
|
DBG_LOG(DBG_TM, "TimeMgr %p has timed out", mgr);
|
|
mgr->Expire();
|
|
|
|
// Make sure events are executed. They depend on the TimerMgr.
|
|
::mgr.Drain();
|
|
|
|
sessions->timer_mgrs.erase(mgr->GetTag());
|
|
delete mgr;
|
|
}
|
|
else
|
|
{
|
|
// Reinstall timer.
|
|
if ( ! is_expire )
|
|
{
|
|
double n = mgr->LastAdvance() +
|
|
timer_mgr_inactivity_timeout;
|
|
timer_mgr->Add(new TimerMgrExpireTimer(n, mgr));
|
|
}
|
|
}
|
|
}
|
|
|
|
NetSessions::NetSessions()
|
|
{
|
|
TypeList* t = new TypeList();
|
|
t->Append(base_type(TYPE_COUNT)); // source IP address
|
|
t->Append(base_type(TYPE_COUNT)); // dest IP address
|
|
t->Append(base_type(TYPE_COUNT)); // source and dest ports
|
|
|
|
ch = new CompositeHash(t);
|
|
|
|
Unref(t);
|
|
|
|
tcp_conns.SetDeleteFunc(bro_obj_delete_func);
|
|
udp_conns.SetDeleteFunc(bro_obj_delete_func);
|
|
fragments.SetDeleteFunc(bro_obj_delete_func);
|
|
|
|
if ( stp_correlate_pair )
|
|
stp_manager = new SteppingStoneManager();
|
|
else
|
|
stp_manager = 0;
|
|
|
|
discarder = new Discarder();
|
|
if ( ! discarder->IsActive() )
|
|
{
|
|
delete discarder;
|
|
discarder = 0;
|
|
}
|
|
|
|
packet_filter = 0;
|
|
|
|
build_backdoor_analyzer =
|
|
backdoor_stats || rlogin_signature_found ||
|
|
telnet_signature_found || ssh_signature_found ||
|
|
root_backdoor_signature_found || ftp_signature_found ||
|
|
napster_signature_found || kazaa_signature_found ||
|
|
http_signature_found || http_proxy_signature_found;
|
|
|
|
dump_this_packet = 0;
|
|
num_packets_processed = 0;
|
|
|
|
if ( OS_version_found )
|
|
{
|
|
SYN_OS_Fingerprinter = new OSFingerprint(SYN_FINGERPRINT_MODE);
|
|
if ( SYN_OS_Fingerprinter->Error() )
|
|
exit(1);
|
|
}
|
|
else
|
|
SYN_OS_Fingerprinter = 0;
|
|
|
|
if ( pkt_profile_mode && pkt_profile_freq > 0 && pkt_profile_file )
|
|
pkt_profiler = new PacketProfiler(pkt_profile_mode,
|
|
pkt_profile_freq, pkt_profile_file->AsFile());
|
|
else
|
|
pkt_profiler = 0;
|
|
|
|
if ( arp_request || arp_reply || bad_arp )
|
|
arp_analyzer = new ARP_Analyzer();
|
|
else
|
|
arp_analyzer = 0;
|
|
|
|
|
|
if ( BifConst::Tunnel::decapsulate_ip || BifConst::Tunnel::decapsulate_udp )
|
|
tunnel_handler = new TunnelHandler(this);
|
|
else
|
|
tunnel_handler = 0;
|
|
}
|
|
|
|
NetSessions::~NetSessions()
|
|
{
|
|
delete ch;
|
|
delete packet_filter;
|
|
delete SYN_OS_Fingerprinter;
|
|
delete pkt_profiler;
|
|
Unref(arp_analyzer);
|
|
}
|
|
|
|
void NetSessions::Done()
|
|
{
|
|
delete stp_manager;
|
|
delete discarder;
|
|
}
|
|
|
|
namespace // private namespace
|
|
{
|
|
bool looks_like_IPv4_packet(int len, const struct ip* ip_hdr)
|
|
{
|
|
if ( len < int(sizeof(struct ip)) )
|
|
return false;
|
|
return ip_hdr->ip_v == 4 && ntohs(ip_hdr->ip_len) == len;
|
|
}
|
|
}
|
|
|
|
void NetSessions::DispatchPacket(double t, const struct pcap_pkthdr* hdr,
|
|
const u_char* pkt, int hdr_size,
|
|
PktSrc* src_ps, PacketSortElement* pkt_elem)
|
|
{
|
|
const struct ip* ip_hdr = 0;
|
|
const u_char* ip_data = 0;
|
|
int proto = 0;
|
|
|
|
if ( hdr->caplen >= hdr_size + sizeof(struct ip) )
|
|
{
|
|
ip_hdr = reinterpret_cast<const struct ip*>(pkt + hdr_size);
|
|
if ( hdr->caplen >= unsigned(hdr_size + (ip_hdr->ip_hl << 2)) )
|
|
ip_data = pkt + hdr_size + (ip_hdr->ip_hl << 2);
|
|
}
|
|
|
|
if ( src_ps->FilterType() == TYPE_FILTER_NORMAL )
|
|
NextPacket(t, hdr, pkt, hdr_size, pkt_elem);
|
|
else
|
|
NextPacketSecondary(t, hdr, pkt, hdr_size, src_ps);
|
|
}
|
|
|
|
void NetSessions::NextPacket(double t, const struct pcap_pkthdr* hdr,
|
|
const u_char* const pkt, int hdr_size,
|
|
PacketSortElement* pkt_elem)
|
|
{
|
|
SegmentProfiler(segment_logger, "processing-packet");
|
|
if ( pkt_profiler )
|
|
pkt_profiler->ProfilePkt(t, hdr->caplen);
|
|
|
|
++num_packets_processed;
|
|
|
|
dump_this_packet = 0;
|
|
|
|
if ( record_all_packets )
|
|
DumpPacket(hdr, pkt);
|
|
|
|
if ( pkt_elem && pkt_elem->IPHdr() )
|
|
// Fast path for "normal" IP packets if an IP_Hdr is
|
|
// already extracted when doing PacketSort. Otherwise
|
|
// the code below tries to extract the IP header, the
|
|
// difference here is that header extraction in
|
|
// PacketSort does not generate Weird events.
|
|
|
|
DoNextPacket(t, hdr, pkt_elem->IPHdr(), pkt, hdr_size);
|
|
|
|
else
|
|
{
|
|
// ### The following isn't really correct. What we *should*
|
|
// do is understanding the different link layers in order to
|
|
// find the network-layer protocol ID. That's a big
|
|
// portability pain, though, unless we just assume everything's
|
|
// Ethernet .... not great, given the potential need to deal
|
|
// with PPP or FDDI (for some older traces). So instead
|
|
// we look to see if what we have is consistent with an
|
|
// IPv4 packet. If not, it's either ARP or IPv6 or weird.
|
|
|
|
uint32 caplen = hdr->caplen - hdr_size;
|
|
if ( caplen < sizeof(struct ip) )
|
|
{
|
|
Weird("truncated_IP", hdr, pkt);
|
|
return;
|
|
}
|
|
|
|
const struct ip* ip = (const struct ip*) (pkt + hdr_size);
|
|
if ( ip->ip_v == 4 )
|
|
{
|
|
IP_Hdr ip_hdr(ip);
|
|
DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size);
|
|
}
|
|
|
|
else if ( arp_analyzer && arp_analyzer->IsARP(pkt, hdr_size) )
|
|
arp_analyzer->NextPacket(t, hdr, pkt, hdr_size);
|
|
|
|
else
|
|
{
|
|
#ifdef BROv6
|
|
IP_Hdr ip_hdr((const struct ip6_hdr*) (pkt + hdr_size));
|
|
DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size);
|
|
#else
|
|
Weird("non_IPv4_packet", hdr, pkt);
|
|
return;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if ( dump_this_packet && ! record_all_packets )
|
|
DumpPacket(hdr, pkt);
|
|
}
|
|
|
|
void NetSessions::NextPacketSecondary(double /* t */, const struct pcap_pkthdr* hdr,
|
|
const u_char* const pkt, int hdr_size,
|
|
const PktSrc* src_ps)
|
|
{
|
|
SegmentProfiler(segment_logger, "processing-secondary-packet");
|
|
|
|
++num_packets_processed;
|
|
|
|
uint32 caplen = hdr->caplen - hdr_size;
|
|
if ( caplen < sizeof(struct ip) )
|
|
{
|
|
Weird("truncated_IP", hdr, pkt);
|
|
return;
|
|
}
|
|
|
|
const struct ip* ip = (const struct ip*) (pkt + hdr_size);
|
|
if ( ip->ip_v == 4 )
|
|
{
|
|
const secondary_program_list& spt = src_ps->ProgramTable();
|
|
|
|
loop_over_list(spt, i)
|
|
{
|
|
SecondaryProgram* sp = spt[i];
|
|
if ( ! net_packet_match(sp->Program(), pkt,
|
|
hdr->len, hdr->caplen) )
|
|
continue;
|
|
|
|
val_list* args = new val_list;
|
|
StringVal* cmd_val =
|
|
new StringVal(sp->Event()->Filter());
|
|
args->append(cmd_val);
|
|
args->append(BuildHeader(ip));
|
|
// ### Need to queue event here.
|
|
sp->Event()->Event()->Call(args);
|
|
delete args;
|
|
}
|
|
}
|
|
}
|
|
|
|
int NetSessions::CheckConnectionTag(Connection* conn)
|
|
{
|
|
if ( current_iosrc->GetCurrentTag() )
|
|
{
|
|
// Packet is tagged.
|
|
if ( conn->GetTimerMgr() == timer_mgr )
|
|
{
|
|
// Connection uses global timer queue. But the
|
|
// packet has a tag that means we got it externally,
|
|
// probably from the Time Machine.
|
|
DBG_LOG(DBG_TM, "got packet with tag %s for already"
|
|
"known connection, reinstantiating",
|
|
current_iosrc->GetCurrentTag()->c_str());
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
// Connection uses local timer queue.
|
|
TimerMgrMap::iterator i =
|
|
timer_mgrs.find(*current_iosrc->GetCurrentTag());
|
|
if ( i != timer_mgrs.end() &&
|
|
conn->GetTimerMgr() != i->second )
|
|
{
|
|
// Connection uses different local queue
|
|
// than the tag for the current packet
|
|
// indicates.
|
|
//
|
|
// This can happen due to:
|
|
// (1) getting same packets with
|
|
// different tags
|
|
// (2) timer mgr having already expired
|
|
DBG_LOG(DBG_TM, "packet ignored due old/inconsistent tag");
|
|
return -1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Packet is not tagged.
|
|
if ( conn->GetTimerMgr() != timer_mgr )
|
|
{
|
|
// Connection does not use the global timer queue. That
|
|
// means that this is a live packet belonging to a
|
|
// connection for which we have already switched to
|
|
// processing external input.
|
|
DBG_LOG(DBG_TM, "packet ignored due to processing it in external data");
|
|
return -1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static bool looks_like_IPv4_packet(int len, const struct ip* ip_hdr)
|
|
{
|
|
if ( (unsigned int) len < sizeof(struct ip) )
|
|
return false;
|
|
|
|
if ( ip_hdr->ip_v == 4 && ntohs(ip_hdr->ip_len) == len )
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
static inline void delete_tunnel_info(TunnelInfo *ti)
|
|
{
|
|
if ( ti )
|
|
delete ti;
|
|
}
|
|
|
|
void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|
const IP_Hdr* ip_hdr, const u_char* const pkt,
|
|
int hdr_size)
|
|
{
|
|
uint32 caplen = hdr->caplen - hdr_size;
|
|
const struct ip* ip4 = ip_hdr->IP4_Hdr();
|
|
|
|
uint32 len = ip_hdr->TotalLen();
|
|
if ( hdr->len < len + hdr_size )
|
|
{
|
|
Weird("truncated_IP", hdr, pkt);
|
|
return;
|
|
}
|
|
|
|
// Ignore if packet matches packet filter.
|
|
if ( packet_filter && packet_filter->Match(ip_hdr, len, caplen) )
|
|
return;
|
|
|
|
int ip_hdr_len = ip_hdr->HdrLen();
|
|
if ( ! ignore_checksums && ip4 &&
|
|
ones_complement_checksum((void*) ip4, ip_hdr_len, 0) != 0xffff )
|
|
{
|
|
Weird("bad_IP_checksum", hdr, pkt);
|
|
return;
|
|
}
|
|
|
|
if ( discarder && discarder->NextPacket(ip_hdr, len, caplen) )
|
|
return;
|
|
|
|
FragReassembler* f = 0;
|
|
uint32 frag_field = ip_hdr->FragField();
|
|
|
|
if ( (frag_field & 0x3fff) != 0 )
|
|
{
|
|
dump_this_packet = 1; // always record fragments
|
|
|
|
if ( caplen < len )
|
|
{
|
|
Weird("incompletely_captured_fragment", ip_hdr);
|
|
|
|
// Don't try to reassemble, that's doomed.
|
|
// Discard all except the first fragment (which
|
|
// is useful in analyzing header-only traces)
|
|
if ( (frag_field & 0x1fff) != 0 )
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
f = NextFragment(t, ip_hdr, pkt + hdr_size, frag_field);
|
|
const IP_Hdr* ih = f->ReassembledPkt();
|
|
if ( ! ih )
|
|
// It didn't reassemble into anything yet.
|
|
return;
|
|
|
|
ip4 = ih->IP4_Hdr();
|
|
ip_hdr = ih;
|
|
|
|
caplen = len = ip_hdr->TotalLen();
|
|
ip_hdr_len = ip_hdr->HdrLen();
|
|
}
|
|
}
|
|
|
|
len -= ip_hdr_len; // remove IP header
|
|
caplen -= ip_hdr_len;
|
|
|
|
TunnelInfo *tunnel_info = 0;
|
|
if ( tunnel_handler )
|
|
{
|
|
tunnel_info = tunnel_handler->DecapsulateTunnel(ip_hdr, len, caplen, hdr, pkt);
|
|
if (tunnel_info)
|
|
{
|
|
ip4 = tunnel_info->child->IP4_Hdr();
|
|
ip_hdr = tunnel_info->child;
|
|
len -= tunnel_info->hdr_len;
|
|
caplen -= tunnel_info->hdr_len;
|
|
}
|
|
}
|
|
|
|
int proto = ip_hdr->NextProto();
|
|
if ( proto != IPPROTO_TCP && proto != IPPROTO_UDP &&
|
|
proto != IPPROTO_ICMP )
|
|
{
|
|
dump_this_packet = 1;
|
|
if ( f )
|
|
Remove(t);
|
|
delete_tunnel_info(tunnel_info);
|
|
return;
|
|
}
|
|
|
|
uint32 min_hdr_len = (proto == IPPROTO_TCP) ? sizeof(struct tcphdr) :
|
|
(proto == IPPROTO_UDP ? sizeof(struct udphdr) : ICMP_MINLEN);
|
|
|
|
if ( len < min_hdr_len )
|
|
{
|
|
Weird("truncated_header", hdr, pkt);
|
|
if ( f )
|
|
Remove(f); // ###
|
|
delete_tunnel_info(tunnel_info);
|
|
return;
|
|
}
|
|
if ( caplen < min_hdr_len )
|
|
{
|
|
Weird("internally_truncated_header", hdr, pkt);
|
|
if ( f )
|
|
Remove(f); // ###
|
|
delete_tunnel_info(tunnel_info);
|
|
return;
|
|
}
|
|
|
|
const u_char* data = ip_hdr->Payload();
|
|
|
|
ConnID id;
|
|
id.src_addr = ip_hdr->SrcAddr();
|
|
id.dst_addr = ip_hdr->DstAddr();
|
|
Dictionary* d = 0;
|
|
bool pass_to_conn_compressor = false;
|
|
|
|
switch ( proto ) {
|
|
case IPPROTO_TCP:
|
|
{
|
|
const struct tcphdr* tp = (const struct tcphdr *) data;
|
|
id.src_port = tp->th_sport;
|
|
id.dst_port = tp->th_dport;
|
|
id.is_one_way = 0;
|
|
d = &tcp_conns;
|
|
pass_to_conn_compressor = ip4 && use_connection_compressor;
|
|
break;
|
|
}
|
|
|
|
case IPPROTO_UDP:
|
|
{
|
|
const struct udphdr* up = (const struct udphdr *) data;
|
|
id.src_port = up->uh_sport;
|
|
id.dst_port = up->uh_dport;
|
|
id.is_one_way = 0;
|
|
d = &udp_conns;
|
|
break;
|
|
}
|
|
|
|
case IPPROTO_ICMP:
|
|
{
|
|
const struct icmp* icmpp = (const struct icmp *) data;
|
|
|
|
id.src_port = icmpp->icmp_type;
|
|
id.dst_port = ICMP_counterpart(icmpp->icmp_type,
|
|
icmpp->icmp_code,
|
|
id.is_one_way);
|
|
|
|
id.src_port = htons(id.src_port);
|
|
id.dst_port = htons(id.dst_port);
|
|
|
|
d = &icmp_conns;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
Weird(fmt("unknown_protocol %d", proto), hdr, pkt);
|
|
if ( f )
|
|
Remove(f);
|
|
delete_tunnel_info(tunnel_info);
|
|
return;
|
|
}
|
|
|
|
HashKey* h = id.BuildConnKey();
|
|
if ( ! h )
|
|
reporter->InternalError("hash computation failed");
|
|
|
|
Connection* conn = 0;
|
|
|
|
// FIXME: The following is getting pretty complex. Need to split up
|
|
// into separate functions.
|
|
if ( pass_to_conn_compressor )
|
|
conn = conn_compressor->NextPacket(t, h, ip_hdr, hdr, pkt);
|
|
else
|
|
{
|
|
conn = (Connection*) d->Lookup(h);
|
|
if ( ! conn )
|
|
{
|
|
conn = NewConn(h, t, &id, data, proto, tunnel_info);
|
|
if ( conn )
|
|
d->Insert(h, conn);
|
|
}
|
|
else
|
|
{
|
|
// We already know that connection.
|
|
int consistent = CheckConnectionTag(conn);
|
|
if ( consistent < 0 )
|
|
{
|
|
delete h;
|
|
if ( f )
|
|
Remove(f);
|
|
delete_tunnel_info(tunnel_info);
|
|
return;
|
|
}
|
|
|
|
if ( ! consistent || conn->IsReuse(t, data) )
|
|
{
|
|
if ( consistent )
|
|
conn->Event(connection_reused, 0);
|
|
|
|
Remove(conn);
|
|
conn = NewConn(h, t, &id, data, proto, tunnel_info);
|
|
if ( conn )
|
|
d->Insert(h, conn);
|
|
}
|
|
else
|
|
delete h;
|
|
}
|
|
|
|
if ( ! conn )
|
|
delete h;
|
|
}
|
|
|
|
if ( ! conn )
|
|
{
|
|
delete_tunnel_info(tunnel_info);
|
|
if ( f )
|
|
Remove(f);
|
|
return;
|
|
}
|
|
|
|
int record_packet = 1; // whether to record the packet at all
|
|
int record_content = 1; // whether to record its data
|
|
|
|
int is_orig = addr_eq(id.src_addr, conn->OrigAddr()) &&
|
|
id.src_port == conn->OrigPort();
|
|
|
|
if ( new_packet && ip4 )
|
|
conn->Event(new_packet, 0, BuildHeader(ip4));
|
|
|
|
conn->NextPacket(t, is_orig, ip_hdr, len, caplen, data,
|
|
record_packet, record_content,
|
|
hdr, pkt, hdr_size);
|
|
|
|
if ( tunnel_info )
|
|
delete tunnel_info;
|
|
if ( f )
|
|
{
|
|
// Above we already recorded the fragment in its entirety.
|
|
f->DeleteTimer();
|
|
Remove(f); // ###
|
|
}
|
|
|
|
else if ( record_packet )
|
|
{
|
|
if ( record_content )
|
|
dump_this_packet = 1; // save the whole thing
|
|
|
|
else
|
|
{
|
|
int hdr_len = data - pkt;
|
|
DumpPacket(hdr, pkt, hdr_len); // just save the header
|
|
}
|
|
}
|
|
}
|
|
|
|
Val* NetSessions::BuildHeader(const struct ip* ip)
|
|
{
|
|
static RecordType* pkt_hdr_type = 0;
|
|
static RecordType* ip_hdr_type = 0;
|
|
static RecordType* tcp_hdr_type = 0;
|
|
static RecordType* udp_hdr_type = 0;
|
|
static RecordType* icmp_hdr_type;
|
|
|
|
if ( ! pkt_hdr_type )
|
|
{
|
|
pkt_hdr_type = internal_type("pkt_hdr")->AsRecordType();
|
|
ip_hdr_type = internal_type("ip_hdr")->AsRecordType();
|
|
tcp_hdr_type = internal_type("tcp_hdr")->AsRecordType();
|
|
udp_hdr_type = internal_type("udp_hdr")->AsRecordType();
|
|
icmp_hdr_type = internal_type("icmp_hdr")->AsRecordType();
|
|
}
|
|
|
|
RecordVal* pkt_hdr = new RecordVal(pkt_hdr_type);
|
|
|
|
RecordVal* ip_hdr = new RecordVal(ip_hdr_type);
|
|
|
|
int ip_hdr_len = ip->ip_hl * 4;
|
|
int ip_pkt_len = ntohs(ip->ip_len);
|
|
|
|
ip_hdr->Assign(0, new Val(ip->ip_hl * 4, TYPE_COUNT));
|
|
ip_hdr->Assign(1, new Val(ip->ip_tos, TYPE_COUNT));
|
|
ip_hdr->Assign(2, new Val(ip_pkt_len, TYPE_COUNT));
|
|
ip_hdr->Assign(3, new Val(ntohs(ip->ip_id), TYPE_COUNT));
|
|
ip_hdr->Assign(4, new Val(ip->ip_ttl, TYPE_COUNT));
|
|
ip_hdr->Assign(5, new Val(ip->ip_p, TYPE_COUNT));
|
|
ip_hdr->Assign(6, new AddrVal(ip->ip_src.s_addr));
|
|
ip_hdr->Assign(7, new AddrVal(ip->ip_dst.s_addr));
|
|
|
|
pkt_hdr->Assign(0, ip_hdr);
|
|
|
|
// L4 header.
|
|
const u_char* data = ((const u_char*) ip) + ip_hdr_len;
|
|
|
|
int proto = ip->ip_p;
|
|
switch ( proto ) {
|
|
case IPPROTO_TCP:
|
|
{
|
|
const struct tcphdr* tp = (const struct tcphdr*) data;
|
|
RecordVal* tcp_hdr = new RecordVal(tcp_hdr_type);
|
|
|
|
int tcp_hdr_len = tp->th_off * 4;
|
|
int data_len = ip_pkt_len - ip_hdr_len - tcp_hdr_len;
|
|
|
|
tcp_hdr->Assign(0, new PortVal(ntohs(tp->th_sport), TRANSPORT_TCP));
|
|
tcp_hdr->Assign(1, new PortVal(ntohs(tp->th_dport), TRANSPORT_TCP));
|
|
tcp_hdr->Assign(2, new Val(uint32(ntohl(tp->th_seq)), TYPE_COUNT));
|
|
tcp_hdr->Assign(3, new Val(uint32(ntohl(tp->th_ack)), TYPE_COUNT));
|
|
tcp_hdr->Assign(4, new Val(tcp_hdr_len, TYPE_COUNT));
|
|
tcp_hdr->Assign(5, new Val(data_len, TYPE_COUNT));
|
|
tcp_hdr->Assign(6, new Val(tp->th_flags, TYPE_COUNT));
|
|
tcp_hdr->Assign(7, new Val(ntohs(tp->th_win), TYPE_COUNT));
|
|
|
|
pkt_hdr->Assign(1, tcp_hdr);
|
|
break;
|
|
}
|
|
|
|
case IPPROTO_UDP:
|
|
{
|
|
const struct udphdr* up = (const struct udphdr*) data;
|
|
RecordVal* udp_hdr = new RecordVal(udp_hdr_type);
|
|
|
|
udp_hdr->Assign(0, new PortVal(ntohs(up->uh_sport), TRANSPORT_UDP));
|
|
udp_hdr->Assign(1, new PortVal(ntohs(up->uh_dport), TRANSPORT_UDP));
|
|
udp_hdr->Assign(2, new Val(ntohs(up->uh_ulen), TYPE_COUNT));
|
|
|
|
pkt_hdr->Assign(2, udp_hdr);
|
|
break;
|
|
}
|
|
|
|
case IPPROTO_ICMP:
|
|
{
|
|
const struct icmp* icmpp = (const struct icmp *) data;
|
|
RecordVal* icmp_hdr = new RecordVal(icmp_hdr_type);
|
|
|
|
icmp_hdr->Assign(0, new Val(icmpp->icmp_type, TYPE_COUNT));
|
|
|
|
pkt_hdr->Assign(3, icmp_hdr);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
// This is not a protocol we understand.
|
|
}
|
|
}
|
|
|
|
return pkt_hdr;
|
|
}
|
|
|
|
FragReassembler* NetSessions::NextFragment(double t, const IP_Hdr* ip,
|
|
const u_char* pkt, uint32 frag_field)
|
|
{
|
|
uint32 src_addr = uint32(ip->SrcAddr4());
|
|
uint32 dst_addr = uint32(ip->DstAddr4());
|
|
uint32 frag_id = ntohs(ip->ID4()); // we actually could skip conv.
|
|
|
|
ListVal* key = new ListVal(TYPE_ANY);
|
|
key->Append(new Val(src_addr, TYPE_COUNT));
|
|
key->Append(new Val(dst_addr, TYPE_COUNT));
|
|
key->Append(new Val(frag_id, TYPE_COUNT));
|
|
|
|
HashKey* h = ch->ComputeHash(key, 1);
|
|
if ( ! h )
|
|
reporter->InternalError("hash computation failed");
|
|
|
|
FragReassembler* f = fragments.Lookup(h);
|
|
if ( ! f )
|
|
{
|
|
f = new FragReassembler(this, ip, pkt, frag_field, h, t);
|
|
fragments.Insert(h, f);
|
|
Unref(key);
|
|
return f;
|
|
}
|
|
|
|
delete h;
|
|
Unref(key);
|
|
|
|
f->AddFragment(t, ip, pkt, frag_field);
|
|
return f;
|
|
}
|
|
|
|
int NetSessions::Get_OS_From_SYN(struct os_type* retval,
|
|
uint16 tot, uint8 DF_flag, uint8 TTL, uint16 WSS,
|
|
uint8 ocnt, uint8* op, uint16 MSS, uint8 win_scale,
|
|
uint32 tstamp, /* uint8 TOS, */ uint32 quirks,
|
|
uint8 ECN) const
|
|
{
|
|
return SYN_OS_Fingerprinter ?
|
|
SYN_OS_Fingerprinter->FindMatch(retval, tot, DF_flag, TTL,
|
|
WSS, ocnt, op, MSS, win_scale, tstamp,
|
|
quirks, ECN) : 0;
|
|
}
|
|
|
|
bool NetSessions::CompareWithPreviousOSMatch(uint32 addr, int id) const
|
|
{
|
|
return SYN_OS_Fingerprinter ?
|
|
SYN_OS_Fingerprinter->CacheMatch(addr, id) : 0;
|
|
}
|
|
|
|
Connection* NetSessions::FindConnection(Val* v)
|
|
{
|
|
BroType* vt = v->Type();
|
|
if ( ! IsRecord(vt->Tag()) )
|
|
return 0;
|
|
|
|
RecordType* vr = vt->AsRecordType();
|
|
const val_list* vl = v->AsRecord();
|
|
|
|
int orig_h, orig_p; // indices into record's value list
|
|
int resp_h, resp_p;
|
|
|
|
if ( vr == conn_id )
|
|
{
|
|
orig_h = 0;
|
|
orig_p = 1;
|
|
resp_h = 2;
|
|
resp_p = 3;
|
|
}
|
|
|
|
else
|
|
{
|
|
// While it's not a conn_id, it may have equivalent fields.
|
|
orig_h = vr->FieldOffset("orig_h");
|
|
resp_h = vr->FieldOffset("resp_h");
|
|
orig_p = vr->FieldOffset("orig_p");
|
|
resp_p = vr->FieldOffset("resp_p");
|
|
|
|
if ( orig_h < 0 || resp_h < 0 || orig_p < 0 || resp_p < 0 )
|
|
return 0;
|
|
|
|
// ### we ought to check that the fields have the right
|
|
// types, too.
|
|
}
|
|
|
|
addr_type orig_addr = (*vl)[orig_h]->AsAddr();
|
|
addr_type resp_addr = (*vl)[resp_h]->AsAddr();
|
|
|
|
PortVal* orig_portv = (*vl)[orig_p]->AsPortVal();
|
|
PortVal* resp_portv = (*vl)[resp_p]->AsPortVal();
|
|
|
|
ConnID id;
|
|
|
|
#ifdef BROv6
|
|
id.src_addr = orig_addr;
|
|
id.dst_addr = resp_addr;
|
|
#else
|
|
id.src_addr = &orig_addr;
|
|
id.dst_addr = &resp_addr;
|
|
#endif
|
|
|
|
id.src_port = htons((unsigned short) orig_portv->Port());
|
|
id.dst_port = htons((unsigned short) resp_portv->Port());
|
|
|
|
id.is_one_way = 0; // ### incorrect for ICMP connections
|
|
|
|
HashKey* h = id.BuildConnKey();
|
|
if ( ! h )
|
|
reporter->InternalError("hash computation failed");
|
|
|
|
Dictionary* d;
|
|
|
|
if ( orig_portv->IsTCP() )
|
|
{
|
|
if ( use_connection_compressor )
|
|
{
|
|
Connection* conn = conn_compressor->Lookup(h);
|
|
delete h;
|
|
return conn;
|
|
}
|
|
else
|
|
d = &tcp_conns;
|
|
}
|
|
else if ( orig_portv->IsUDP() )
|
|
d = &udp_conns;
|
|
else if ( orig_portv->IsICMP() )
|
|
d = &icmp_conns;
|
|
else
|
|
{
|
|
// 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;
|
|
|
|
return conn;
|
|
}
|
|
|
|
void NetSessions::Remove(Connection* c)
|
|
{
|
|
HashKey* k = c->Key();
|
|
if ( k )
|
|
{
|
|
c->CancelTimers();
|
|
|
|
TCP_Analyzer* ta = (TCP_Analyzer*) c->GetRootAnalyzer();
|
|
if ( ta && c->ConnTransport() == TRANSPORT_TCP )
|
|
{
|
|
assert(ta->GetTag() == AnalyzerTag::TCP);
|
|
TCP_Endpoint* to = ta->Orig();
|
|
TCP_Endpoint* tr = ta->Resp();
|
|
|
|
tcp_stats.StateLeft(to->state, tr->state);
|
|
}
|
|
|
|
if ( c->IsPersistent() )
|
|
persistence_serializer->Unregister(c);
|
|
|
|
c->Done();
|
|
|
|
if ( connection_state_remove )
|
|
c->Event(connection_state_remove, 0);
|
|
|
|
// Zero out c's copy of the key, so that if c has been Ref()'d
|
|
// up, we know on a future call to Remove() that it's no
|
|
// longer in the dictionary.
|
|
c->ClearKey();
|
|
|
|
switch ( c->ConnTransport() ) {
|
|
case TRANSPORT_TCP:
|
|
if ( use_connection_compressor &&
|
|
conn_compressor->Remove(k) )
|
|
// Note, if the Remove() returned false
|
|
// then the compressor doesn't know about
|
|
// this connection, which *should* mean that
|
|
// we never gave it the connection in the
|
|
// first place, and thus we should check
|
|
// the regular TCP table instead.
|
|
;
|
|
|
|
else if ( ! tcp_conns.RemoveEntry(k) )
|
|
reporter->InternalError("connection missing");
|
|
break;
|
|
|
|
case TRANSPORT_UDP:
|
|
if ( ! udp_conns.RemoveEntry(k) )
|
|
reporter->InternalError("connection missing");
|
|
break;
|
|
|
|
case TRANSPORT_ICMP:
|
|
if ( ! icmp_conns.RemoveEntry(k) )
|
|
reporter->InternalError("connection missing");
|
|
break;
|
|
|
|
case TRANSPORT_UNKNOWN:
|
|
reporter->InternalError("unknown transport when removing connection");
|
|
break;
|
|
}
|
|
|
|
Unref(c);
|
|
delete k;
|
|
}
|
|
}
|
|
|
|
void NetSessions::Remove(FragReassembler* f)
|
|
{
|
|
HashKey* k = f->Key();
|
|
if ( ! k )
|
|
reporter->InternalError("fragment block not in dictionary");
|
|
|
|
if ( ! fragments.RemoveEntry(k) )
|
|
reporter->InternalError("fragment block missing");
|
|
|
|
Unref(f);
|
|
}
|
|
|
|
void NetSessions::Insert(Connection* c)
|
|
{
|
|
assert(c->Key());
|
|
|
|
Connection* old = 0;
|
|
|
|
switch ( c->ConnTransport() ) {
|
|
// Remove first. Otherwise the dictioanry would still
|
|
// reference the old key for already existing connections.
|
|
|
|
case TRANSPORT_TCP:
|
|
if ( use_connection_compressor )
|
|
old = conn_compressor->Insert(c);
|
|
else
|
|
{
|
|
old = (Connection*) tcp_conns.Remove(c->Key());
|
|
tcp_conns.Insert(c->Key(), c);
|
|
}
|
|
break;
|
|
|
|
case TRANSPORT_UDP:
|
|
old = (Connection*) udp_conns.Remove(c->Key());
|
|
udp_conns.Insert(c->Key(), c);
|
|
break;
|
|
|
|
case TRANSPORT_ICMP:
|
|
old = (Connection*) icmp_conns.Remove(c->Key());
|
|
icmp_conns.Insert(c->Key(), c);
|
|
break;
|
|
|
|
default:
|
|
reporter->InternalError("unknown connection type");
|
|
}
|
|
|
|
if ( old && old != c )
|
|
{
|
|
// Some clean-ups similar to those in Remove() (but invisible
|
|
// to the script layer).
|
|
old->CancelTimers();
|
|
if ( old->IsPersistent() )
|
|
persistence_serializer->Unregister(old);
|
|
delete old->Key();
|
|
old->ClearKey();
|
|
Unref(old);
|
|
}
|
|
}
|
|
|
|
void NetSessions::Drain()
|
|
{
|
|
if ( use_connection_compressor )
|
|
conn_compressor->Drain();
|
|
|
|
IterCookie* cookie = tcp_conns.InitForIteration();
|
|
Connection* tc;
|
|
|
|
while ( (tc = tcp_conns.NextEntry(cookie)) )
|
|
{
|
|
tc->Done();
|
|
tc->Event(connection_state_remove, 0);
|
|
}
|
|
|
|
cookie = udp_conns.InitForIteration();
|
|
Connection* uc;
|
|
|
|
while ( (uc = udp_conns.NextEntry(cookie)) )
|
|
{
|
|
uc->Done();
|
|
uc->Event(connection_state_remove, 0);
|
|
}
|
|
|
|
cookie = icmp_conns.InitForIteration();
|
|
Connection* ic;
|
|
|
|
while ( (ic = icmp_conns.NextEntry(cookie)) )
|
|
{
|
|
ic->Done();
|
|
ic->Event(connection_state_remove, 0);
|
|
}
|
|
|
|
ExpireTimerMgrs();
|
|
}
|
|
|
|
void NetSessions::GetStats(SessionStats& s) const
|
|
{
|
|
s.num_TCP_conns = tcp_conns.Length();
|
|
s.num_UDP_conns = udp_conns.Length();
|
|
s.num_ICMP_conns = icmp_conns.Length();
|
|
s.num_fragments = fragments.Length();
|
|
s.num_packets = num_packets_processed;
|
|
s.num_timers = timer_mgr->Size();
|
|
s.num_events_queued = num_events_queued;
|
|
s.num_events_dispatched = num_events_dispatched;
|
|
|
|
s.max_TCP_conns = tcp_conns.MaxLength();
|
|
s.max_UDP_conns = udp_conns.MaxLength();
|
|
s.max_ICMP_conns = icmp_conns.MaxLength();
|
|
s.max_fragments = fragments.MaxLength();
|
|
s.max_timers = timer_mgr->PeakSize();
|
|
}
|
|
|
|
Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id,
|
|
const u_char* data, int proto, TunnelInfo* tunnel_info)
|
|
{
|
|
// FIXME: This should be cleaned up a bit, it's too protocol-specific.
|
|
// But I'm not yet sure what the right abstraction for these things is.
|
|
int src_h = ntohs(id->src_port);
|
|
int dst_h = ntohs(id->dst_port);
|
|
int flags = 0;
|
|
TunnelParent *tunnel_parent = 0;
|
|
|
|
// Hmm... This is not great.
|
|
TransportProto tproto;
|
|
switch ( proto ) {
|
|
case IPPROTO_ICMP:
|
|
tproto = TRANSPORT_ICMP;
|
|
break;
|
|
case IPPROTO_TCP:
|
|
tproto = TRANSPORT_TCP;
|
|
break;
|
|
case IPPROTO_UDP:
|
|
tproto = TRANSPORT_UDP;
|
|
break;
|
|
default:
|
|
reporter->InternalError("unknown transport protocol");
|
|
break;
|
|
};
|
|
|
|
if ( tproto == TRANSPORT_TCP )
|
|
{
|
|
const struct tcphdr* tp = (const struct tcphdr*) data;
|
|
flags = tp->th_flags;
|
|
}
|
|
|
|
bool flip = false;
|
|
|
|
if ( ! WantConnection(src_h, dst_h, tproto, flags, flip) )
|
|
return 0;
|
|
|
|
if ( flip )
|
|
{
|
|
// Make a guess that we're seeing the tail half of
|
|
// an analyzable connection.
|
|
ConnID flip_id = *id;
|
|
|
|
const uint32* ta = flip_id.src_addr;
|
|
flip_id.src_addr = flip_id.dst_addr;
|
|
flip_id.dst_addr = ta;
|
|
|
|
uint32 t = flip_id.src_port;
|
|
flip_id.src_port = flip_id.dst_port;
|
|
flip_id.dst_port = t;
|
|
|
|
id = &flip_id;
|
|
}
|
|
|
|
if ( tunnel_info )
|
|
tunnel_parent = new TunnelParent(&(tunnel_info->parent));
|
|
|
|
Connection* conn = new Connection(this, k, t, id, tunnel_parent);
|
|
conn->SetTransport(tproto);
|
|
dpm->BuildInitialAnalyzerTree(tproto, conn, data);
|
|
|
|
bool external = conn->IsExternal();
|
|
|
|
if ( external )
|
|
conn->AppendAddl(fmt("tag=%s",
|
|
conn->GetTimerMgr()->GetTag().c_str()));
|
|
|
|
// If the connection compressor is active, it takes care of the
|
|
// new_connection/connection_external events for TCP connections.
|
|
if ( new_connection &&
|
|
(tproto != TRANSPORT_TCP || ! use_connection_compressor) )
|
|
{
|
|
conn->Event(new_connection, 0);
|
|
|
|
if ( external )
|
|
{
|
|
val_list* vl = new val_list(2);
|
|
vl->append(conn->BuildConnVal());
|
|
vl->append(new StringVal(conn->GetTimerMgr()->GetTag().c_str()));
|
|
conn->ConnectionEvent(connection_external, 0, vl);
|
|
}
|
|
}
|
|
|
|
return conn;
|
|
}
|
|
|
|
bool NetSessions::IsLikelyServerPort(uint32 port, TransportProto proto) const
|
|
{
|
|
// We keep a cached in-core version of the table to speed up the lookup.
|
|
static set<bro_uint_t> port_cache;
|
|
static bool have_cache = false;
|
|
|
|
if ( ! have_cache )
|
|
{
|
|
ListVal* lv = likely_server_ports->ConvertToPureList();
|
|
for ( int i = 0; i < lv->Length(); i++ )
|
|
port_cache.insert(lv->Index(i)->InternalUnsigned());
|
|
have_cache = true;
|
|
Unref(lv);
|
|
}
|
|
|
|
// We exploit our knowledge of PortVal's internal storage mechanism
|
|
// here.
|
|
if ( proto == TRANSPORT_TCP )
|
|
port |= TCP_PORT_MASK;
|
|
else if ( proto == TRANSPORT_UDP )
|
|
port |= UDP_PORT_MASK;
|
|
else if ( proto == TRANSPORT_ICMP )
|
|
port |= ICMP_PORT_MASK;
|
|
|
|
return port_cache.find(port) != port_cache.end();
|
|
}
|
|
|
|
bool NetSessions::WantConnection(uint16 src_port, uint16 dst_port,
|
|
TransportProto transport_proto,
|
|
uint8 tcp_flags, bool& flip_roles)
|
|
{
|
|
flip_roles = false;
|
|
|
|
if ( transport_proto == TRANSPORT_TCP )
|
|
{
|
|
if ( ! (tcp_flags & TH_SYN) || (tcp_flags & TH_ACK) )
|
|
{
|
|
// The new connection is starting either without a SYN,
|
|
// or with a SYN ack. This means it's a partial connection.
|
|
if ( ! partial_connection_ok )
|
|
return false;
|
|
|
|
if ( tcp_flags & TH_SYN && ! tcp_SYN_ack_ok )
|
|
return false;
|
|
|
|
// Try to guess true responder by the port numbers.
|
|
// (We might also think that for SYN acks we could
|
|
// safely flip the roles, but that doesn't work
|
|
// for stealth scans.)
|
|
if ( IsLikelyServerPort(src_port, TRANSPORT_TCP) )
|
|
{ // connection is a candidate for flipping
|
|
if ( IsLikelyServerPort(dst_port, TRANSPORT_TCP) )
|
|
// Hmmm, both source and destination
|
|
// are plausible. Heuristic: flip only
|
|
// if (1) this isn't a SYN ACK (to avoid
|
|
// confusing stealth scans) and
|
|
// (2) dest port > src port (to favor
|
|
// more plausible servers).
|
|
flip_roles = ! (tcp_flags & TH_SYN) && src_port < dst_port;
|
|
else
|
|
// Source is plausible, destination isn't.
|
|
flip_roles = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
else if ( transport_proto == TRANSPORT_UDP )
|
|
flip_roles =
|
|
IsLikelyServerPort(src_port, TRANSPORT_UDP) &&
|
|
! IsLikelyServerPort(dst_port, TRANSPORT_UDP);
|
|
|
|
return true;
|
|
}
|
|
|
|
TimerMgr* NetSessions::LookupTimerMgr(const TimerMgr::Tag* tag, bool create)
|
|
{
|
|
if ( ! tag )
|
|
{
|
|
DBG_LOG(DBG_TM, "no tag, using global timer mgr %p", timer_mgr);
|
|
return timer_mgr;
|
|
}
|
|
|
|
TimerMgrMap::iterator i = timer_mgrs.find(*tag);
|
|
if ( i != timer_mgrs.end() )
|
|
{
|
|
DBG_LOG(DBG_TM, "tag %s, using non-global timer mgr %p", tag->c_str(), i->second);
|
|
return i->second;
|
|
}
|
|
else
|
|
{
|
|
if ( ! create )
|
|
return 0;
|
|
|
|
// Create new queue for tag.
|
|
TimerMgr* mgr = new CQ_TimerMgr(*tag);
|
|
DBG_LOG(DBG_TM, "tag %s, creating new non-global timer mgr %p", tag->c_str(), mgr);
|
|
timer_mgrs.insert(TimerMgrMap::value_type(*tag, mgr));
|
|
double t = timer_mgr->Time() + timer_mgr_inactivity_timeout;
|
|
timer_mgr->Add(new TimerMgrExpireTimer(t, mgr));
|
|
return mgr;
|
|
}
|
|
}
|
|
|
|
void NetSessions::ExpireTimerMgrs()
|
|
{
|
|
for ( TimerMgrMap::iterator i = timer_mgrs.begin();
|
|
i != timer_mgrs.end(); ++i )
|
|
{
|
|
i->second->Expire();
|
|
delete i->second;
|
|
}
|
|
}
|
|
|
|
void NetSessions::DumpPacket(const struct pcap_pkthdr* hdr,
|
|
const u_char* pkt, int len)
|
|
{
|
|
if ( ! pkt_dumper )
|
|
return;
|
|
|
|
if ( len == 0 )
|
|
pkt_dumper->Dump(hdr, pkt);
|
|
else
|
|
{
|
|
struct pcap_pkthdr h = *hdr;
|
|
h.caplen = len;
|
|
if ( h.caplen > hdr->caplen )
|
|
reporter->InternalError("bad modified caplen");
|
|
pkt_dumper->Dump(&h, pkt);
|
|
}
|
|
}
|
|
|
|
void NetSessions::Internal(const char* msg, const struct pcap_pkthdr* hdr,
|
|
const u_char* pkt)
|
|
{
|
|
DumpPacket(hdr, pkt);
|
|
reporter->InternalError("%s", msg);
|
|
}
|
|
|
|
void NetSessions::Weird(const char* name,
|
|
const struct pcap_pkthdr* hdr, const u_char* pkt)
|
|
{
|
|
if ( hdr )
|
|
dump_this_packet = 1;
|
|
|
|
reporter->Weird(name);
|
|
}
|
|
|
|
void NetSessions::Weird(const char* name, const IP_Hdr* ip)
|
|
{
|
|
reporter->Weird(ip->SrcAddr(), ip->DstAddr(), name);
|
|
}
|
|
|
|
unsigned int NetSessions::ConnectionMemoryUsage()
|
|
{
|
|
unsigned int mem = 0;
|
|
|
|
if ( terminating )
|
|
// Connections have been flushed already.
|
|
return 0;
|
|
|
|
IterCookie* cookie = tcp_conns.InitForIteration();
|
|
Connection* tc;
|
|
|
|
while ( (tc = tcp_conns.NextEntry(cookie)) )
|
|
mem += tc->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();
|
|
|
|
return mem;
|
|
}
|
|
|
|
unsigned int NetSessions::ConnectionMemoryUsageConnVals()
|
|
{
|
|
unsigned int mem = 0;
|
|
|
|
if ( terminating )
|
|
// Connections have been flushed already.
|
|
return 0;
|
|
|
|
IterCookie* cookie = tcp_conns.InitForIteration();
|
|
Connection* tc;
|
|
|
|
while ( (tc = tcp_conns.NextEntry(cookie)) )
|
|
mem += tc->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();
|
|
|
|
return mem;
|
|
}
|
|
|
|
unsigned int NetSessions::MemoryAllocation()
|
|
{
|
|
if ( terminating )
|
|
// Connections have been flushed already.
|
|
return 0;
|
|
|
|
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))
|
|
+ fragments.MemoryAllocation() - padded_sizeof(fragments)
|
|
// FIXME: MemoryAllocation() not implemented for rest.
|
|
;
|
|
}
|