mirror of
https://github.com/zeek/zeek.git
synced 2025-10-13 20:18:20 +00:00
Merge branch 'topic/robin/tunnels-merge'
* topic/robin/tunnels-merge: (51 commits) Updating baselines and NEWS. Remove &synchronized from Tunnel::active table. Refactor of interal tunnel analysis code. Add state management of NetSessions's IP tunnel map. Add "encap_hdr_size" option back in. Script-layer tunnel interface cleanup. Fix performance problem checking packet encapsulation. (addresses #830) Adding a SOCKS test case. Updating DataSeries baselines. Moving my todos over to the tracker ticket. Extend weird names that occur in core packet processing during decapsulation. Add Teredo analysis option to reduce false positive decapsulation. Just some cleanup/documentation of new tunnel-handling code. Memory leak fixes Add a config.h definition for IPPROTO_IPV4. Add AYIYA tunnel decapsulation unit test. Add Teredo-specific events. Refactor some of the NetSessions routines that recurse on IP packets. Add independent options to toggle the different decapsulation methods Add more sanity checks before recursing on encapsulated IP packets. ... Conflicts: src/event.bif
This commit is contained in:
commit
b096168318
93 changed files with 2638 additions and 236 deletions
24
src/AYIYA.cc
Normal file
24
src/AYIYA.cc
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "AYIYA.h"
|
||||
|
||||
AYIYA_Analyzer::AYIYA_Analyzer(Connection* conn)
|
||||
: Analyzer(AnalyzerTag::AYIYA, conn)
|
||||
{
|
||||
interp = new binpac::AYIYA::AYIYA_Conn(this);
|
||||
}
|
||||
|
||||
AYIYA_Analyzer::~AYIYA_Analyzer()
|
||||
{
|
||||
delete interp;
|
||||
}
|
||||
|
||||
void AYIYA_Analyzer::Done()
|
||||
{
|
||||
Analyzer::Done();
|
||||
Event(udp_session_done);
|
||||
}
|
||||
|
||||
void AYIYA_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, int seq, const IP_Hdr* ip, int caplen)
|
||||
{
|
||||
Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen);
|
||||
interp->NewData(orig, data, data + len);
|
||||
}
|
29
src/AYIYA.h
Normal file
29
src/AYIYA.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef AYIYA_h
|
||||
#define AYIYA_h
|
||||
|
||||
#include "ayiya_pac.h"
|
||||
|
||||
class AYIYA_Analyzer : public Analyzer {
|
||||
public:
|
||||
AYIYA_Analyzer(Connection* conn);
|
||||
virtual ~AYIYA_Analyzer();
|
||||
|
||||
virtual void Done();
|
||||
virtual void DeliverPacket(int len, const u_char* data, bool orig,
|
||||
int seq, const IP_Hdr* ip, int caplen);
|
||||
|
||||
static Analyzer* InstantiateAnalyzer(Connection* conn)
|
||||
{ return new AYIYA_Analyzer(conn); }
|
||||
|
||||
static bool Available()
|
||||
{ return BifConst::Tunnel::enable_ayiya &&
|
||||
BifConst::Tunnel::max_depth > 0; }
|
||||
|
||||
protected:
|
||||
friend class AnalyzerTimer;
|
||||
void ExpireTimer(double t);
|
||||
|
||||
binpac::AYIYA::AYIYA_Conn* interp;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -4,6 +4,7 @@
|
|||
#include "PIA.h"
|
||||
#include "Event.h"
|
||||
|
||||
#include "AYIYA.h"
|
||||
#include "BackDoor.h"
|
||||
#include "BitTorrent.h"
|
||||
#include "BitTorrentTracker.h"
|
||||
|
@ -33,9 +34,11 @@
|
|||
#include "NFS.h"
|
||||
#include "Portmap.h"
|
||||
#include "POP3.h"
|
||||
#include "SOCKS.h"
|
||||
#include "SSH.h"
|
||||
#include "SSL.h"
|
||||
#include "Syslog-binpac.h"
|
||||
#include "Teredo.h"
|
||||
#include "ConnSizeAnalyzer.h"
|
||||
|
||||
// Keep same order here as in AnalyzerTag definition!
|
||||
|
@ -127,6 +130,16 @@ const Analyzer::Config Analyzer::analyzer_configs[] = {
|
|||
Syslog_Analyzer_binpac::InstantiateAnalyzer,
|
||||
Syslog_Analyzer_binpac::Available, 0, false },
|
||||
|
||||
{ AnalyzerTag::AYIYA, "AYIYA",
|
||||
AYIYA_Analyzer::InstantiateAnalyzer,
|
||||
AYIYA_Analyzer::Available, 0, false },
|
||||
{ AnalyzerTag::SOCKS, "SOCKS",
|
||||
SOCKS_Analyzer::InstantiateAnalyzer,
|
||||
SOCKS_Analyzer::Available, 0, false },
|
||||
{ AnalyzerTag::Teredo, "TEREDO",
|
||||
Teredo_Analyzer::InstantiateAnalyzer,
|
||||
Teredo_Analyzer::Available, 0, false },
|
||||
|
||||
{ AnalyzerTag::File, "FILE", File_Analyzer::InstantiateAnalyzer,
|
||||
File_Analyzer::Available, 0, false },
|
||||
{ AnalyzerTag::Backdoor, "BACKDOOR",
|
||||
|
|
|
@ -215,6 +215,11 @@ public:
|
|||
// analyzer, even if the method is called multiple times.
|
||||
virtual void ProtocolConfirmation();
|
||||
|
||||
// Return whether the analyzer previously called ProtocolConfirmation()
|
||||
// at least once before.
|
||||
bool ProtocolConfirmed() const
|
||||
{ return protocol_confirmed; }
|
||||
|
||||
// Report that we found a significant protocol violation which might
|
||||
// indicate that the analyzed data is in fact not the expected
|
||||
// protocol. The protocol_violation event is raised once per call to
|
||||
|
@ -338,6 +343,10 @@ private:
|
|||
for ( analyzer_list::iterator var = the_kids.begin(); \
|
||||
var != the_kids.end(); var++ )
|
||||
|
||||
#define LOOP_OVER_GIVEN_CONST_CHILDREN(var, the_kids) \
|
||||
for ( analyzer_list::const_iterator var = the_kids.begin(); \
|
||||
var != the_kids.end(); var++ )
|
||||
|
||||
class SupportAnalyzer : public Analyzer {
|
||||
public:
|
||||
SupportAnalyzer(AnalyzerTag::Tag tag, Connection* conn, bool arg_orig)
|
||||
|
|
|
@ -33,11 +33,15 @@ namespace AnalyzerTag {
|
|||
DHCP_BINPAC, DNS_TCP_BINPAC, DNS_UDP_BINPAC,
|
||||
HTTP_BINPAC, SSL, SYSLOG_BINPAC,
|
||||
|
||||
// Decapsulation analyzers.
|
||||
AYIYA,
|
||||
SOCKS,
|
||||
Teredo,
|
||||
|
||||
// Other
|
||||
File, Backdoor, InterConn, SteppingStone, TCPStats,
|
||||
ConnSize,
|
||||
|
||||
|
||||
// Support-analyzers
|
||||
Contents, ContentLine, NVT, Zip, Contents_DNS, Contents_NCP,
|
||||
Contents_NetbiosSSN, Contents_Rlogin, Contents_Rsh,
|
||||
|
|
|
@ -187,6 +187,9 @@ endmacro(BINPAC_TARGET)
|
|||
|
||||
binpac_target(binpac-lib.pac)
|
||||
binpac_target(binpac_bro-lib.pac)
|
||||
|
||||
binpac_target(ayiya.pac
|
||||
ayiya-protocol.pac ayiya-analyzer.pac)
|
||||
binpac_target(bittorrent.pac
|
||||
bittorrent-protocol.pac bittorrent-analyzer.pac)
|
||||
binpac_target(dce_rpc.pac
|
||||
|
@ -206,6 +209,8 @@ binpac_target(netflow.pac
|
|||
netflow-protocol.pac netflow-analyzer.pac)
|
||||
binpac_target(smb.pac
|
||||
smb-protocol.pac smb-pipe.pac smb-mailslot.pac)
|
||||
binpac_target(socks.pac
|
||||
socks-protocol.pac socks-analyzer.pac)
|
||||
binpac_target(ssl.pac
|
||||
ssl-defs.pac ssl-protocol.pac ssl-analyzer.pac)
|
||||
binpac_target(syslog.pac
|
||||
|
@ -277,6 +282,7 @@ set(bro_SRCS
|
|||
Anon.cc
|
||||
ARP.cc
|
||||
Attr.cc
|
||||
AYIYA.cc
|
||||
BackDoor.cc
|
||||
Base64.cc
|
||||
BitTorrent.cc
|
||||
|
@ -375,6 +381,7 @@ set(bro_SRCS
|
|||
SmithWaterman.cc
|
||||
SMB.cc
|
||||
SMTP.cc
|
||||
SOCKS.cc
|
||||
SSH.cc
|
||||
SSL.cc
|
||||
Scope.cc
|
||||
|
@ -391,9 +398,11 @@ set(bro_SRCS
|
|||
TCP_Endpoint.cc
|
||||
TCP_Reassembler.cc
|
||||
Telnet.cc
|
||||
Teredo.cc
|
||||
Timer.cc
|
||||
Traverse.cc
|
||||
Trigger.cc
|
||||
TunnelEncapsulation.cc
|
||||
Type.cc
|
||||
UDP.cc
|
||||
Val.cc
|
||||
|
|
39
src/Conn.cc
39
src/Conn.cc
|
@ -13,6 +13,7 @@
|
|||
#include "Timer.h"
|
||||
#include "PIA.h"
|
||||
#include "binpac.h"
|
||||
#include "TunnelEncapsulation.h"
|
||||
|
||||
void ConnectionTimer::Init(Connection* arg_conn, timer_func arg_timer,
|
||||
int arg_do_expire)
|
||||
|
@ -112,7 +113,7 @@ unsigned int Connection::external_connections = 0;
|
|||
IMPLEMENT_SERIAL(Connection, SER_CONNECTION);
|
||||
|
||||
Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id,
|
||||
uint32 flow)
|
||||
uint32 flow, const EncapsulationStack* arg_encap)
|
||||
{
|
||||
sessions = s;
|
||||
key = k;
|
||||
|
@ -160,6 +161,11 @@ Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id,
|
|||
|
||||
uid = 0; // Will set later.
|
||||
|
||||
if ( arg_encap )
|
||||
encapsulation = new EncapsulationStack(*arg_encap);
|
||||
else
|
||||
encapsulation = 0;
|
||||
|
||||
if ( conn_timer_mgr )
|
||||
{
|
||||
++external_connections;
|
||||
|
@ -187,12 +193,40 @@ Connection::~Connection()
|
|||
delete key;
|
||||
delete root_analyzer;
|
||||
delete conn_timer_mgr;
|
||||
delete encapsulation;
|
||||
|
||||
--current_connections;
|
||||
if ( conn_timer_mgr )
|
||||
--external_connections;
|
||||
}
|
||||
|
||||
void Connection::CheckEncapsulation(const EncapsulationStack* arg_encap)
|
||||
{
|
||||
if ( encapsulation && arg_encap )
|
||||
{
|
||||
if ( *encapsulation != *arg_encap )
|
||||
{
|
||||
Event(tunnel_changed, 0, arg_encap->GetVectorVal());
|
||||
delete encapsulation;
|
||||
encapsulation = new EncapsulationStack(*arg_encap);
|
||||
}
|
||||
}
|
||||
|
||||
else if ( encapsulation )
|
||||
{
|
||||
EncapsulationStack empty;
|
||||
Event(tunnel_changed, 0, empty.GetVectorVal());
|
||||
delete encapsulation;
|
||||
encapsulation = 0;
|
||||
}
|
||||
|
||||
else if ( arg_encap )
|
||||
{
|
||||
Event(tunnel_changed, 0, arg_encap->GetVectorVal());
|
||||
encapsulation = new EncapsulationStack(*arg_encap);
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::Done()
|
||||
{
|
||||
finished = 1;
|
||||
|
@ -349,6 +383,9 @@ RecordVal* Connection::BuildConnVal()
|
|||
|
||||
char tmp[20];
|
||||
conn_val->Assign(9, new StringVal(uitoa_n(uid, tmp, sizeof(tmp), 62)));
|
||||
|
||||
if ( encapsulation && encapsulation->Depth() > 0 )
|
||||
conn_val->Assign(10, encapsulation->GetVectorVal());
|
||||
}
|
||||
|
||||
if ( root_analyzer )
|
||||
|
|
16
src/Conn.h
16
src/Conn.h
|
@ -13,6 +13,7 @@
|
|||
#include "RuleMatcher.h"
|
||||
#include "AnalyzerTags.h"
|
||||
#include "IPAddr.h"
|
||||
#include "TunnelEncapsulation.h"
|
||||
|
||||
class Connection;
|
||||
class ConnectionTimer;
|
||||
|
@ -51,9 +52,16 @@ class Analyzer;
|
|||
class Connection : public BroObj {
|
||||
public:
|
||||
Connection(NetSessions* s, HashKey* k, double t, const ConnID* id,
|
||||
uint32 flow);
|
||||
uint32 flow, const EncapsulationStack* arg_encap);
|
||||
virtual ~Connection();
|
||||
|
||||
// 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.
|
||||
void CheckEncapsulation(const EncapsulationStack* 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
|
||||
|
@ -242,6 +250,11 @@ public:
|
|||
|
||||
void SetUID(uint64 arg_uid) { uid = arg_uid; }
|
||||
|
||||
uint64 GetUID() const { return uid; }
|
||||
|
||||
const EncapsulationStack* GetEncapsulation() const
|
||||
{ return encapsulation; }
|
||||
|
||||
void CheckFlowLabel(bool is_orig, uint32 flow_label);
|
||||
|
||||
protected:
|
||||
|
@ -279,6 +292,7 @@ protected:
|
|||
double inactivity_timeout;
|
||||
RecordVal* conn_val;
|
||||
LoginConn* login_conn; // either nil, or this
|
||||
const EncapsulationStack* encapsulation; // tunnels
|
||||
int suppress_event; // suppress certain events to once per conn.
|
||||
|
||||
unsigned int installed_status_timer:1;
|
||||
|
|
|
@ -31,7 +31,6 @@ int tcp_SYN_ack_ok;
|
|||
int tcp_match_undelivered;
|
||||
|
||||
int encap_hdr_size;
|
||||
int udp_tunnel_port;
|
||||
|
||||
double frag_timeout;
|
||||
|
||||
|
@ -328,8 +327,6 @@ void init_net_var()
|
|||
|
||||
encap_hdr_size = opt_internal_int("encap_hdr_size");
|
||||
|
||||
udp_tunnel_port = opt_internal_int("udp_tunnel_port") & ~UDP_PORT_MASK;
|
||||
|
||||
frag_timeout = opt_internal_double("frag_timeout");
|
||||
|
||||
tcp_SYN_timeout = opt_internal_double("tcp_SYN_timeout");
|
||||
|
|
|
@ -34,7 +34,6 @@ extern int tcp_SYN_ack_ok;
|
|||
extern int tcp_match_undelivered;
|
||||
|
||||
extern int encap_hdr_size;
|
||||
extern int udp_tunnel_port;
|
||||
|
||||
extern double frag_timeout;
|
||||
|
||||
|
|
79
src/SOCKS.cc
Normal file
79
src/SOCKS.cc
Normal file
|
@ -0,0 +1,79 @@
|
|||
#include "SOCKS.h"
|
||||
#include "socks_pac.h"
|
||||
#include "TCP_Reassembler.h"
|
||||
|
||||
SOCKS_Analyzer::SOCKS_Analyzer(Connection* conn)
|
||||
: TCP_ApplicationAnalyzer(AnalyzerTag::SOCKS, conn)
|
||||
{
|
||||
interp = new binpac::SOCKS::SOCKS_Conn(this);
|
||||
orig_done = resp_done = false;
|
||||
pia = 0;
|
||||
}
|
||||
|
||||
SOCKS_Analyzer::~SOCKS_Analyzer()
|
||||
{
|
||||
delete interp;
|
||||
}
|
||||
|
||||
void SOCKS_Analyzer::EndpointDone(bool orig)
|
||||
{
|
||||
if ( orig )
|
||||
orig_done = true;
|
||||
else
|
||||
resp_done = true;
|
||||
}
|
||||
|
||||
void SOCKS_Analyzer::Done()
|
||||
{
|
||||
TCP_ApplicationAnalyzer::Done();
|
||||
|
||||
interp->FlowEOF(true);
|
||||
interp->FlowEOF(false);
|
||||
}
|
||||
|
||||
void SOCKS_Analyzer::EndpointEOF(TCP_Reassembler* endp)
|
||||
{
|
||||
TCP_ApplicationAnalyzer::EndpointEOF(endp);
|
||||
interp->FlowEOF(endp->IsOrig());
|
||||
}
|
||||
|
||||
void SOCKS_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
|
||||
{
|
||||
TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
|
||||
|
||||
assert(TCP());
|
||||
|
||||
if ( TCP()->IsPartial() )
|
||||
// punt on partial.
|
||||
return;
|
||||
|
||||
if ( orig_done && resp_done )
|
||||
{
|
||||
// Finished decapsulating tunnel layer. Now do standard processing
|
||||
// with the rest of the conneciton.
|
||||
//
|
||||
// Note that we assume that no payload data arrives before both endpoints
|
||||
// are done with there part of the SOCKS protocol.
|
||||
|
||||
if ( ! pia )
|
||||
{
|
||||
pia = new PIA_TCP(Conn());
|
||||
AddChildAnalyzer(pia);
|
||||
pia->FirstPacket(true, 0);
|
||||
pia->FirstPacket(false, 0);
|
||||
}
|
||||
|
||||
ForwardStream(len, data, orig);
|
||||
}
|
||||
else
|
||||
{
|
||||
interp->NewData(orig, data, data + len);
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKS_Analyzer::Undelivered(int seq, int len, bool orig)
|
||||
{
|
||||
TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
|
||||
interp->NewGap(orig, len);
|
||||
}
|
||||
|
45
src/SOCKS.h
Normal file
45
src/SOCKS.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
#ifndef socks_h
|
||||
#define socks_h
|
||||
|
||||
// SOCKS v4 analyzer.
|
||||
|
||||
#include "TCP.h"
|
||||
#include "PIA.h"
|
||||
|
||||
namespace binpac {
|
||||
namespace SOCKS {
|
||||
class SOCKS_Conn;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SOCKS_Analyzer : public TCP_ApplicationAnalyzer {
|
||||
public:
|
||||
SOCKS_Analyzer(Connection* conn);
|
||||
~SOCKS_Analyzer();
|
||||
|
||||
void EndpointDone(bool orig);
|
||||
|
||||
virtual void Done();
|
||||
virtual void DeliverStream(int len, const u_char* data, bool orig);
|
||||
virtual void Undelivered(int seq, int len, bool orig);
|
||||
virtual void EndpointEOF(TCP_Reassembler* endp);
|
||||
|
||||
static Analyzer* InstantiateAnalyzer(Connection* conn)
|
||||
{ return new SOCKS_Analyzer(conn); }
|
||||
|
||||
static bool Available()
|
||||
{
|
||||
return socks_request || socks_reply;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
bool orig_done;
|
||||
bool resp_done;
|
||||
|
||||
PIA_TCP *pia;
|
||||
binpac::SOCKS::SOCKS_Conn* interp;
|
||||
};
|
||||
|
||||
#endif
|
297
src/Sessions.cc
297
src/Sessions.cc
|
@ -30,6 +30,7 @@
|
|||
#include "DPM.h"
|
||||
|
||||
#include "PacketSort.h"
|
||||
#include "TunnelEncapsulation.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
|
||||
|
@ -67,6 +68,26 @@ void TimerMgrExpireTimer::Dispatch(double t, int is_expire)
|
|||
}
|
||||
}
|
||||
|
||||
void IPTunnelTimer::Dispatch(double t, int is_expire)
|
||||
{
|
||||
NetSessions::IPTunnelMap::const_iterator it =
|
||||
sessions->ip_tunnels.find(tunnel_idx);
|
||||
|
||||
if ( it == sessions->ip_tunnels.end() )
|
||||
return;
|
||||
|
||||
double last_active = it->second.second;
|
||||
double inactive_time = t > last_active ? t - last_active : 0;
|
||||
|
||||
if ( inactive_time >= BifConst::Tunnel::ip_tunnel_timeout )
|
||||
// tunnel activity timed out, delete it from map
|
||||
sessions->ip_tunnels.erase(tunnel_idx);
|
||||
|
||||
else if ( ! is_expire )
|
||||
// tunnel activity didn't timeout, schedule another timer
|
||||
timer_mgr->Add(new IPTunnelTimer(t, tunnel_idx));
|
||||
}
|
||||
|
||||
NetSessions::NetSessions()
|
||||
{
|
||||
TypeList* t = new TypeList();
|
||||
|
@ -142,16 +163,6 @@ void NetSessions::Done()
|
|||
{
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -168,60 +179,8 @@ void NetSessions::DispatchPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
}
|
||||
|
||||
if ( encap_hdr_size > 0 && ip_data )
|
||||
{
|
||||
// We're doing tunnel encapsulation. Check whether there's
|
||||
// a particular associated port.
|
||||
//
|
||||
// Should we discourage the use of encap_hdr_size for UDP
|
||||
// tunnneling? It is probably better handled by enabling
|
||||
// BifConst::parse_udp_tunnels instead of specifying a fixed
|
||||
// encap_hdr_size.
|
||||
if ( udp_tunnel_port > 0 )
|
||||
{
|
||||
ASSERT(ip_hdr);
|
||||
if ( ip_hdr->ip_p == IPPROTO_UDP )
|
||||
{
|
||||
const struct udphdr* udp_hdr =
|
||||
reinterpret_cast<const struct udphdr*>
|
||||
(ip_data);
|
||||
|
||||
if ( ntohs(udp_hdr->uh_dport) == udp_tunnel_port )
|
||||
{
|
||||
// A match.
|
||||
hdr_size += encap_hdr_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
// Blanket encapsulation
|
||||
hdr_size += encap_hdr_size;
|
||||
}
|
||||
|
||||
// Check IP packets encapsulated through UDP tunnels.
|
||||
// Specifying a udp_tunnel_port is optional but recommended (to avoid
|
||||
// the cost of checking every UDP packet).
|
||||
else if ( BifConst::parse_udp_tunnels && ip_data && ip_hdr->ip_p == IPPROTO_UDP )
|
||||
{
|
||||
const struct udphdr* udp_hdr =
|
||||
reinterpret_cast<const struct udphdr*>(ip_data);
|
||||
|
||||
if ( udp_tunnel_port == 0 || // 0 matches any port
|
||||
udp_tunnel_port == ntohs(udp_hdr->uh_dport) )
|
||||
{
|
||||
const u_char* udp_data =
|
||||
ip_data + sizeof(struct udphdr);
|
||||
const struct ip* ip_encap =
|
||||
reinterpret_cast<const struct ip*>(udp_data);
|
||||
const int ip_encap_len =
|
||||
ntohs(udp_hdr->uh_ulen) - sizeof(struct udphdr);
|
||||
const int ip_encap_caplen =
|
||||
hdr->caplen - (udp_data - pkt);
|
||||
|
||||
if ( looks_like_IPv4_packet(ip_encap_len, ip_encap) )
|
||||
hdr_size = udp_data - pkt;
|
||||
}
|
||||
}
|
||||
// Blanket encapsulation
|
||||
hdr_size += encap_hdr_size;
|
||||
|
||||
if ( src_ps->FilterType() == TYPE_FILTER_NORMAL )
|
||||
NextPacket(t, hdr, pkt, hdr_size, pkt_elem);
|
||||
|
@ -251,7 +210,7 @@ void NetSessions::NextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
// difference here is that header extraction in
|
||||
// PacketSort does not generate Weird events.
|
||||
|
||||
DoNextPacket(t, hdr, pkt_elem->IPHdr(), pkt, hdr_size);
|
||||
DoNextPacket(t, hdr, pkt_elem->IPHdr(), pkt, hdr_size, 0);
|
||||
|
||||
else
|
||||
{
|
||||
|
@ -276,7 +235,7 @@ void NetSessions::NextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
if ( ip->ip_v == 4 )
|
||||
{
|
||||
IP_Hdr ip_hdr(ip, false);
|
||||
DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size);
|
||||
DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size, 0);
|
||||
}
|
||||
|
||||
else if ( ip->ip_v == 6 )
|
||||
|
@ -288,7 +247,7 @@ void NetSessions::NextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
}
|
||||
|
||||
IP_Hdr ip_hdr((const struct ip6_hdr*) (pkt + hdr_size), false, caplen);
|
||||
DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size);
|
||||
DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size, 0);
|
||||
}
|
||||
|
||||
else if ( ARP_Analyzer::IsARP(pkt, hdr_size) )
|
||||
|
@ -410,7 +369,7 @@ int NetSessions::CheckConnectionTag(Connection* conn)
|
|||
|
||||
void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||
const IP_Hdr* ip_hdr, const u_char* const pkt,
|
||||
int hdr_size)
|
||||
int hdr_size, const EncapsulationStack* encapsulation)
|
||||
{
|
||||
uint32 caplen = hdr->caplen - hdr_size;
|
||||
const struct ip* ip4 = ip_hdr->IP4_Hdr();
|
||||
|
@ -418,7 +377,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
uint32 len = ip_hdr->TotalLen();
|
||||
if ( hdr->len < len + hdr_size )
|
||||
{
|
||||
Weird("truncated_IP", hdr, pkt);
|
||||
Weird("truncated_IP", hdr, pkt, encapsulation);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -430,7 +389,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
if ( ! ignore_checksums && ip4 &&
|
||||
ones_complement_checksum((void*) ip4, ip_hdr_len, 0) != 0xffff )
|
||||
{
|
||||
Weird("bad_IP_checksum", hdr, pkt);
|
||||
Weird("bad_IP_checksum", hdr, pkt, encapsulation);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -445,7 +404,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
|
||||
if ( caplen < len )
|
||||
{
|
||||
Weird("incompletely_captured_fragment", ip_hdr);
|
||||
Weird("incompletely_captured_fragment", ip_hdr, encapsulation);
|
||||
|
||||
// Don't try to reassemble, that's doomed.
|
||||
// Discard all except the first fragment (which
|
||||
|
@ -472,7 +431,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
len -= ip_hdr_len; // remove IP header
|
||||
caplen -= ip_hdr_len;
|
||||
|
||||
// We stop building the chain when seeing IPPROTO_ESP so if it's
|
||||
// We stop building the chain when seeing IPPROTO_ESP so if it's
|
||||
// there, it's always the last.
|
||||
if ( ip_hdr->LastHeader() == IPPROTO_ESP )
|
||||
{
|
||||
|
@ -497,7 +456,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
|
||||
if ( ! ignore_checksums && mobility_header_checksum(ip_hdr) != 0xffff )
|
||||
{
|
||||
Weird("bad_MH_checksum", hdr, pkt);
|
||||
Weird("bad_MH_checksum", hdr, pkt, encapsulation);
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
@ -510,7 +469,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
}
|
||||
|
||||
if ( ip_hdr->NextProto() != IPPROTO_NONE )
|
||||
Weird("mobility_piggyback", hdr, pkt);
|
||||
Weird("mobility_piggyback", hdr, pkt, encapsulation);
|
||||
|
||||
Remove(f);
|
||||
return;
|
||||
|
@ -519,7 +478,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
|
||||
int proto = ip_hdr->NextProto();
|
||||
|
||||
if ( CheckHeaderTrunc(proto, len, caplen, hdr, pkt) )
|
||||
if ( CheckHeaderTrunc(proto, len, caplen, hdr, pkt, encapsulation) )
|
||||
{
|
||||
Remove(f);
|
||||
return;
|
||||
|
@ -585,8 +544,82 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
break;
|
||||
}
|
||||
|
||||
case IPPROTO_IPV4:
|
||||
case IPPROTO_IPV6:
|
||||
{
|
||||
if ( ! BifConst::Tunnel::enable_ip )
|
||||
{
|
||||
Weird("IP_tunnel", ip_hdr, encapsulation);
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( encapsulation &&
|
||||
encapsulation->Depth() >= BifConst::Tunnel::max_depth )
|
||||
{
|
||||
Weird("exceeded_tunnel_max_depth", ip_hdr, encapsulation);
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for a valid inner packet first.
|
||||
IP_Hdr* inner = 0;
|
||||
int result = ParseIPPacket(caplen, data, proto, inner);
|
||||
|
||||
if ( result < 0 )
|
||||
Weird("truncated_inner_IP", ip_hdr, encapsulation);
|
||||
|
||||
else if ( result > 0 )
|
||||
Weird("inner_IP_payload_length_mismatch", ip_hdr, encapsulation);
|
||||
|
||||
if ( result != 0 )
|
||||
{
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
||||
// Look up to see if we've already seen this IP tunnel, identified
|
||||
// by the pair of IP addresses, so that we can always associate the
|
||||
// same UID with it.
|
||||
IPPair tunnel_idx;
|
||||
if ( ip_hdr->SrcAddr() < ip_hdr->DstAddr() )
|
||||
tunnel_idx = IPPair(ip_hdr->SrcAddr(), ip_hdr->DstAddr());
|
||||
else
|
||||
tunnel_idx = IPPair(ip_hdr->DstAddr(), ip_hdr->SrcAddr());
|
||||
|
||||
IPTunnelMap::iterator it = ip_tunnels.find(tunnel_idx);
|
||||
|
||||
if ( it == ip_tunnels.end() )
|
||||
{
|
||||
EncapsulatingConn ec(ip_hdr->SrcAddr(), ip_hdr->DstAddr());
|
||||
ip_tunnels[tunnel_idx] = TunnelActivity(ec, network_time);
|
||||
timer_mgr->Add(new IPTunnelTimer(network_time, tunnel_idx));
|
||||
}
|
||||
else
|
||||
it->second.second = network_time;
|
||||
|
||||
DoNextInnerPacket(t, hdr, inner, encapsulation,
|
||||
ip_tunnels[tunnel_idx].first);
|
||||
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
||||
case IPPROTO_NONE:
|
||||
{
|
||||
// If the packet is encapsulated in Teredo, then it was a bubble and
|
||||
// the Teredo analyzer may have raised an event for that, else we're
|
||||
// not sure the reason for the No Next header in the packet.
|
||||
if ( ! ( encapsulation &&
|
||||
encapsulation->LastType() == BifEnum::Tunnel::TEREDO ) )
|
||||
Weird("ipv6_no_next", hdr, pkt);
|
||||
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
Weird(fmt("unknown_protocol_%d", proto), hdr, pkt);
|
||||
Weird(fmt("unknown_protocol_%d", proto), hdr, pkt, encapsulation);
|
||||
Remove(f);
|
||||
return;
|
||||
}
|
||||
|
@ -602,7 +635,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
conn = (Connection*) d->Lookup(h);
|
||||
if ( ! conn )
|
||||
{
|
||||
conn = NewConn(h, t, &id, data, proto, ip_hdr->FlowLabel());
|
||||
conn = NewConn(h, t, &id, data, proto, ip_hdr->FlowLabel(), encapsulation);
|
||||
if ( conn )
|
||||
d->Insert(h, conn);
|
||||
}
|
||||
|
@ -623,12 +656,15 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
conn->Event(connection_reused, 0);
|
||||
|
||||
Remove(conn);
|
||||
conn = NewConn(h, t, &id, data, proto, ip_hdr->FlowLabel());
|
||||
conn = NewConn(h, t, &id, data, proto, ip_hdr->FlowLabel(), encapsulation);
|
||||
if ( conn )
|
||||
d->Insert(h, conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete h;
|
||||
conn->CheckEncapsulation(encapsulation);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! conn )
|
||||
|
@ -682,8 +718,74 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
|||
}
|
||||
}
|
||||
|
||||
void NetSessions::DoNextInnerPacket(double t, const struct pcap_pkthdr* hdr,
|
||||
const IP_Hdr* inner, const EncapsulationStack* prev,
|
||||
const EncapsulatingConn& ec)
|
||||
{
|
||||
struct pcap_pkthdr fake_hdr;
|
||||
fake_hdr.caplen = fake_hdr.len = inner->TotalLen();
|
||||
|
||||
if ( hdr )
|
||||
fake_hdr.ts = hdr->ts;
|
||||
else
|
||||
{
|
||||
fake_hdr.ts.tv_sec = (time_t) network_time;
|
||||
fake_hdr.ts.tv_usec = (suseconds_t)
|
||||
((network_time - (double)fake_hdr.ts.tv_sec) * 1000000);
|
||||
}
|
||||
|
||||
const u_char* pkt = 0;
|
||||
|
||||
if ( inner->IP4_Hdr() )
|
||||
pkt = (const u_char*) inner->IP4_Hdr();
|
||||
else
|
||||
pkt = (const u_char*) inner->IP6_Hdr();
|
||||
|
||||
EncapsulationStack* outer = prev ?
|
||||
new EncapsulationStack(*prev) : new EncapsulationStack();
|
||||
outer->Add(ec);
|
||||
|
||||
DoNextPacket(t, &fake_hdr, inner, pkt, 0, outer);
|
||||
|
||||
delete inner;
|
||||
delete outer;
|
||||
}
|
||||
|
||||
int NetSessions::ParseIPPacket(int caplen, const u_char* const pkt, int proto,
|
||||
IP_Hdr*& inner)
|
||||
{
|
||||
if ( proto == IPPROTO_IPV6 )
|
||||
{
|
||||
if ( caplen < (int)sizeof(struct ip6_hdr) )
|
||||
return -1;
|
||||
|
||||
inner = new IP_Hdr((const struct ip6_hdr*) pkt, false, caplen);
|
||||
}
|
||||
|
||||
else if ( proto == IPPROTO_IPV4 )
|
||||
{
|
||||
if ( caplen < (int)sizeof(struct ip) )
|
||||
return -1;
|
||||
|
||||
inner = new IP_Hdr((const struct ip*) pkt, false);
|
||||
}
|
||||
|
||||
else
|
||||
reporter->InternalError("Bad IP protocol version in DoNextInnerPacket");
|
||||
|
||||
if ( (uint32)caplen != inner->TotalLen() )
|
||||
{
|
||||
delete inner;
|
||||
inner = 0;
|
||||
return (uint32)caplen < inner->TotalLen() ? -1 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool NetSessions::CheckHeaderTrunc(int proto, uint32 len, uint32 caplen,
|
||||
const struct pcap_pkthdr* h, const u_char* p)
|
||||
const struct pcap_pkthdr* h,
|
||||
const u_char* p, const EncapsulationStack* encap)
|
||||
{
|
||||
uint32 min_hdr_len = 0;
|
||||
switch ( proto ) {
|
||||
|
@ -693,22 +795,32 @@ bool NetSessions::CheckHeaderTrunc(int proto, uint32 len, uint32 caplen,
|
|||
case IPPROTO_UDP:
|
||||
min_hdr_len = sizeof(struct udphdr);
|
||||
break;
|
||||
case IPPROTO_IPV4:
|
||||
min_hdr_len = sizeof(struct ip);
|
||||
break;
|
||||
case IPPROTO_IPV6:
|
||||
min_hdr_len = sizeof(struct ip6_hdr);
|
||||
break;
|
||||
case IPPROTO_NONE:
|
||||
min_hdr_len = 0;
|
||||
break;
|
||||
case IPPROTO_ICMP:
|
||||
case IPPROTO_ICMPV6:
|
||||
default:
|
||||
// Use for all other packets.
|
||||
min_hdr_len = ICMP_MINLEN;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( len < min_hdr_len )
|
||||
{
|
||||
Weird("truncated_header", h, p);
|
||||
Weird("truncated_header", h, p, encap);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( caplen < min_hdr_len )
|
||||
{
|
||||
Weird("internally_truncated_header", h, p);
|
||||
Weird("internally_truncated_header", h, p, encap);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1004,7 +1116,8 @@ void NetSessions::GetStats(SessionStats& s) const
|
|||
}
|
||||
|
||||
Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id,
|
||||
const u_char* data, int proto, uint32 flow_label)
|
||||
const u_char* data, int proto, uint32 flow_label,
|
||||
const EncapsulationStack* encapsulation)
|
||||
{
|
||||
// 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.
|
||||
|
@ -1060,7 +1173,7 @@ Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id,
|
|||
id = &flip_id;
|
||||
}
|
||||
|
||||
Connection* conn = new Connection(this, k, t, id, flow_label);
|
||||
Connection* conn = new Connection(this, k, t, id, flow_label, encapsulation);
|
||||
conn->SetTransport(tproto);
|
||||
dpm->BuildInitialAnalyzerTree(tproto, conn, data);
|
||||
|
||||
|
@ -1224,18 +1337,26 @@ void NetSessions::Internal(const char* msg, const struct pcap_pkthdr* hdr,
|
|||
reporter->InternalError("%s", msg);
|
||||
}
|
||||
|
||||
void NetSessions::Weird(const char* name,
|
||||
const struct pcap_pkthdr* hdr, const u_char* pkt)
|
||||
void NetSessions::Weird(const char* name, const struct pcap_pkthdr* hdr,
|
||||
const u_char* pkt, const EncapsulationStack* encap)
|
||||
{
|
||||
if ( hdr )
|
||||
dump_this_packet = 1;
|
||||
|
||||
reporter->Weird(name);
|
||||
if ( encap && encap->LastType() != BifEnum::Tunnel::NONE )
|
||||
reporter->Weird(fmt("%s_in_tunnel", name));
|
||||
else
|
||||
reporter->Weird(name);
|
||||
}
|
||||
|
||||
void NetSessions::Weird(const char* name, const IP_Hdr* ip)
|
||||
void NetSessions::Weird(const char* name, const IP_Hdr* ip,
|
||||
const EncapsulationStack* encap)
|
||||
{
|
||||
reporter->Weird(ip->SrcAddr(), ip->DstAddr(), name);
|
||||
if ( encap && encap->LastType() != BifEnum::Tunnel::NONE )
|
||||
reporter->Weird(ip->SrcAddr(), ip->DstAddr(),
|
||||
fmt("%s_in_tunnel", name));
|
||||
else
|
||||
reporter->Weird(ip->SrcAddr(), ip->DstAddr(), name);
|
||||
}
|
||||
|
||||
unsigned int NetSessions::ConnectionMemoryUsage()
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
#include "PacketFilter.h"
|
||||
#include "Stats.h"
|
||||
#include "NetVar.h"
|
||||
#include "TunnelEncapsulation.h"
|
||||
#include <utility>
|
||||
|
||||
struct pcap_pkthdr;
|
||||
|
||||
class EncapsulationStack;
|
||||
class Connection;
|
||||
class ConnID;
|
||||
class OSFingerprint;
|
||||
|
@ -105,9 +108,10 @@ public:
|
|||
|
||||
void GetStats(SessionStats& s) const;
|
||||
|
||||
void Weird(const char* name,
|
||||
const struct pcap_pkthdr* hdr, const u_char* pkt);
|
||||
void Weird(const char* name, const IP_Hdr* ip);
|
||||
void Weird(const char* name, const struct pcap_pkthdr* hdr,
|
||||
const u_char* pkt, const EncapsulationStack* encap = 0);
|
||||
void Weird(const char* name, const IP_Hdr* ip,
|
||||
const EncapsulationStack* encap = 0);
|
||||
|
||||
PacketFilter* GetPacketFilter()
|
||||
{
|
||||
|
@ -131,6 +135,48 @@ public:
|
|||
icmp_conns.Length();
|
||||
}
|
||||
|
||||
void DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||
const IP_Hdr* ip_hdr, const u_char* const pkt,
|
||||
int hdr_size, const EncapsulationStack* encapsulation);
|
||||
|
||||
/**
|
||||
* Wrapper that recurses on DoNextPacket for encapsulated IP packets.
|
||||
*
|
||||
* @param t Network time.
|
||||
* @param hdr If the outer pcap header is available, this pointer can be set
|
||||
* so that the fake pcap header passed to DoNextPacket will use
|
||||
* the same timeval. The caplen and len fields of the fake pcap
|
||||
* header are always set to the TotalLength() of \a inner.
|
||||
* @param inner Pointer to IP header wrapper of the inner packet, ownership
|
||||
* of the pointer's memory is assumed by this function.
|
||||
* @param prev Any previous encapsulation stack of the caller, not including
|
||||
* the most-recently found depth of encapsulation.
|
||||
* @param ec The most-recently found depth of encapsulation.
|
||||
*/
|
||||
void DoNextInnerPacket(double t, const struct pcap_pkthdr* hdr,
|
||||
const IP_Hdr* inner, const EncapsulationStack* prev,
|
||||
const EncapsulatingConn& ec);
|
||||
|
||||
/**
|
||||
* Returns a wrapper IP_Hdr object if \a pkt appears to be a valid IPv4
|
||||
* or IPv6 header based on whether it's long enough to contain such a header
|
||||
* and also that the payload length field of that header matches the actual
|
||||
* length of \a pkt given by \a caplen.
|
||||
*
|
||||
* @param caplen The length of \a pkt in bytes.
|
||||
* @param pkt The inner IP packet data.
|
||||
* @param proto Either IPPROTO_IPV6 or IPPROTO_IPV4 to indicate which IP
|
||||
* protocol \a pkt corresponds to.
|
||||
* @param inner The inner IP packet wrapper pointer to be allocated/assigned
|
||||
* if \a pkt looks like a valid IP packet.
|
||||
* @return 0 If the inner IP packet appeared valid in which case the caller
|
||||
* is responsible for deallocating \a inner, else -1 if \a caplen
|
||||
* is greater than the supposed IP packet's payload length field or
|
||||
* 1 if \a caplen is less than the supposed packet's payload length.
|
||||
*/
|
||||
int ParseIPPacket(int caplen, const u_char* const pkt, int proto,
|
||||
IP_Hdr*& inner);
|
||||
|
||||
unsigned int ConnectionMemoryUsage();
|
||||
unsigned int ConnectionMemoryUsageConnVals();
|
||||
unsigned int MemoryAllocation();
|
||||
|
@ -140,9 +186,11 @@ protected:
|
|||
friend class RemoteSerializer;
|
||||
friend class ConnCompressor;
|
||||
friend class TimerMgrExpireTimer;
|
||||
friend class IPTunnelTimer;
|
||||
|
||||
Connection* NewConn(HashKey* k, double t, const ConnID* id,
|
||||
const u_char* data, int proto, uint32 flow_label);
|
||||
const u_char* data, int proto, uint32 flow_lable,
|
||||
const EncapsulationStack* encapsulation);
|
||||
|
||||
// Check whether the tag of the current packet is consistent with
|
||||
// the given connection. Returns:
|
||||
|
@ -173,10 +221,6 @@ protected:
|
|||
const u_char* const pkt, int hdr_size,
|
||||
PacketSortElement* pkt_elem);
|
||||
|
||||
void DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||
const IP_Hdr* ip_hdr, const u_char* const pkt,
|
||||
int hdr_size);
|
||||
|
||||
void NextPacketSecondary(double t, const struct pcap_pkthdr* hdr,
|
||||
const u_char* const pkt, int hdr_size,
|
||||
const PktSrc* src_ps);
|
||||
|
@ -194,7 +238,8 @@ protected:
|
|||
// from lower-level headers or the length actually captured is less
|
||||
// than that protocol's minimum header size.
|
||||
bool CheckHeaderTrunc(int proto, uint32 len, uint32 caplen,
|
||||
const struct pcap_pkthdr* hdr, const u_char* pkt);
|
||||
const struct pcap_pkthdr* hdr, const u_char* pkt,
|
||||
const EncapsulationStack* encap);
|
||||
|
||||
CompositeHash* ch;
|
||||
PDict(Connection) tcp_conns;
|
||||
|
@ -202,6 +247,11 @@ protected:
|
|||
PDict(Connection) icmp_conns;
|
||||
PDict(FragReassembler) fragments;
|
||||
|
||||
typedef pair<IPAddr, IPAddr> IPPair;
|
||||
typedef pair<EncapsulatingConn, double> TunnelActivity;
|
||||
typedef std::map<IPPair, TunnelActivity> IPTunnelMap;
|
||||
IPTunnelMap ip_tunnels;
|
||||
|
||||
ARP_Analyzer* arp_analyzer;
|
||||
|
||||
SteppingStoneManager* stp_manager;
|
||||
|
@ -219,6 +269,21 @@ protected:
|
|||
TimerMgrMap timer_mgrs;
|
||||
};
|
||||
|
||||
|
||||
class IPTunnelTimer : public Timer {
|
||||
public:
|
||||
IPTunnelTimer(double t, NetSessions::IPPair p)
|
||||
: Timer(t + BifConst::Tunnel::ip_tunnel_timeout,
|
||||
TIMER_IP_TUNNEL_INACTIVITY), tunnel_idx(p) {}
|
||||
|
||||
~IPTunnelTimer() {}
|
||||
|
||||
void Dispatch(double t, int is_expire);
|
||||
|
||||
protected:
|
||||
NetSessions::IPPair tunnel_idx;
|
||||
};
|
||||
|
||||
// Manager for the currently active sessions.
|
||||
extern NetSessions* sessions;
|
||||
|
||||
|
|
228
src/Teredo.cc
Normal file
228
src/Teredo.cc
Normal file
|
@ -0,0 +1,228 @@
|
|||
|
||||
#include "Teredo.h"
|
||||
#include "IP.h"
|
||||
#include "Reporter.h"
|
||||
|
||||
void Teredo_Analyzer::Done()
|
||||
{
|
||||
Analyzer::Done();
|
||||
Event(udp_session_done);
|
||||
}
|
||||
|
||||
bool TeredoEncapsulation::DoParse(const u_char* data, int& len,
|
||||
bool found_origin, bool found_auth)
|
||||
{
|
||||
if ( len < 2 )
|
||||
{
|
||||
Weird("truncated_Teredo");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16 tag = ntohs((*((const uint16*)data)));
|
||||
|
||||
if ( tag == 0 )
|
||||
{
|
||||
// Origin Indication
|
||||
if ( found_origin )
|
||||
// can't have multiple origin indications
|
||||
return false;
|
||||
|
||||
if ( len < 8 )
|
||||
{
|
||||
Weird("truncated_Teredo_origin_indication");
|
||||
return false;
|
||||
}
|
||||
|
||||
origin_indication = data;
|
||||
len -= 8;
|
||||
data += 8;
|
||||
return DoParse(data, len, true, found_auth);
|
||||
}
|
||||
|
||||
else if ( tag == 1 )
|
||||
{
|
||||
// Authentication
|
||||
if ( found_origin || found_auth )
|
||||
// can't have multiple authentication headers and can't come after
|
||||
// an origin indication
|
||||
return false;
|
||||
|
||||
if ( len < 4 )
|
||||
{
|
||||
Weird("truncated_Teredo_authentication");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 id_len = data[2];
|
||||
uint8 au_len = data[3];
|
||||
uint16 tot_len = 4 + id_len + au_len + 8 + 1;
|
||||
|
||||
if ( len < tot_len )
|
||||
{
|
||||
Weird("truncated_Teredo_authentication");
|
||||
return false;
|
||||
}
|
||||
|
||||
auth = data;
|
||||
len -= tot_len;
|
||||
data += tot_len;
|
||||
return DoParse(data, len, found_origin, true);
|
||||
}
|
||||
|
||||
else if ( ((tag & 0xf000)>>12) == 6 )
|
||||
{
|
||||
// IPv6
|
||||
if ( len < 40 )
|
||||
{
|
||||
Weird("truncated_IPv6_in_Teredo");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( len - 40 != ntohs(((const struct ip6_hdr*)data)->ip6_plen) )
|
||||
{
|
||||
Weird("Teredo_payload_len_mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
inner_ip = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
RecordVal* TeredoEncapsulation::BuildVal(const IP_Hdr* inner) const
|
||||
{
|
||||
static RecordType* teredo_hdr_type = 0;
|
||||
static RecordType* teredo_auth_type = 0;
|
||||
static RecordType* teredo_origin_type = 0;
|
||||
|
||||
if ( ! teredo_hdr_type )
|
||||
{
|
||||
teredo_hdr_type = internal_type("teredo_hdr")->AsRecordType();
|
||||
teredo_auth_type = internal_type("teredo_auth")->AsRecordType();
|
||||
teredo_origin_type = internal_type("teredo_origin")->AsRecordType();
|
||||
}
|
||||
|
||||
RecordVal* teredo_hdr = new RecordVal(teredo_hdr_type);
|
||||
|
||||
if ( auth )
|
||||
{
|
||||
RecordVal* teredo_auth = new RecordVal(teredo_auth_type);
|
||||
uint8 id_len = *((uint8*)(auth + 2));
|
||||
uint8 au_len = *((uint8*)(auth + 3));
|
||||
uint64 nonce = ntohll(*((uint64*)(auth + 4 + id_len + au_len)));
|
||||
uint8 conf = *((uint8*)(auth + 4 + id_len + au_len + 8));
|
||||
teredo_auth->Assign(0, new StringVal(
|
||||
new BroString(auth + 4, id_len, 1)));
|
||||
teredo_auth->Assign(1, new StringVal(
|
||||
new BroString(auth + 4 + id_len, au_len, 1)));
|
||||
teredo_auth->Assign(2, new Val(nonce, TYPE_COUNT));
|
||||
teredo_auth->Assign(3, new Val(conf, TYPE_COUNT));
|
||||
teredo_hdr->Assign(0, teredo_auth);
|
||||
}
|
||||
|
||||
if ( origin_indication )
|
||||
{
|
||||
RecordVal* teredo_origin = new RecordVal(teredo_origin_type);
|
||||
uint16 port = ntohs(*((uint16*)(origin_indication + 2))) ^ 0xFFFF;
|
||||
uint32 addr = ntohl(*((uint32*)(origin_indication + 4))) ^ 0xFFFFFFFF;
|
||||
teredo_origin->Assign(0, new PortVal(port, TRANSPORT_UDP));
|
||||
teredo_origin->Assign(1, new AddrVal(htonl(addr)));
|
||||
teredo_hdr->Assign(1, teredo_origin);
|
||||
}
|
||||
|
||||
teredo_hdr->Assign(2, inner->BuildPktHdrVal());
|
||||
return teredo_hdr;
|
||||
}
|
||||
|
||||
void Teredo_Analyzer::DeliverPacket(int len, const u_char* data, bool orig,
|
||||
int seq, const IP_Hdr* ip, int caplen)
|
||||
{
|
||||
Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen);
|
||||
|
||||
TeredoEncapsulation te(this);
|
||||
|
||||
if ( ! te.Parse(data, len) )
|
||||
{
|
||||
ProtocolViolation("Bad Teredo encapsulation", (const char*) data, len);
|
||||
return;
|
||||
}
|
||||
|
||||
const EncapsulationStack* e = Conn()->GetEncapsulation();
|
||||
|
||||
if ( e && e->Depth() >= BifConst::Tunnel::max_depth )
|
||||
{
|
||||
Weird("tunnel_depth");
|
||||
return;
|
||||
}
|
||||
|
||||
IP_Hdr* inner = 0;
|
||||
int rslt = sessions->ParseIPPacket(len, te.InnerIP(), IPPROTO_IPV6, inner);
|
||||
|
||||
if ( rslt == 0 )
|
||||
{
|
||||
if ( BifConst::Tunnel::yielding_teredo_decapsulation &&
|
||||
! ProtocolConfirmed() )
|
||||
{
|
||||
// Only confirm the Teredo tunnel and start decapsulating packets
|
||||
// when no other sibling analyzer thinks it's already parsing the
|
||||
// right protocol.
|
||||
bool sibling_has_confirmed = false;
|
||||
if ( Parent() )
|
||||
{
|
||||
LOOP_OVER_GIVEN_CONST_CHILDREN(i, Parent()->GetChildren())
|
||||
{
|
||||
if ( (*i)->ProtocolConfirmed() )
|
||||
sibling_has_confirmed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! sibling_has_confirmed )
|
||||
ProtocolConfirmation();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Aggressively decapsulate anything with valid Teredo encapsulation
|
||||
ProtocolConfirmation();
|
||||
}
|
||||
}
|
||||
|
||||
else if ( rslt < 0 )
|
||||
ProtocolViolation("Truncated Teredo", (const char*) data, len);
|
||||
|
||||
else
|
||||
ProtocolViolation("Teredo payload length", (const char*) data, len);
|
||||
|
||||
if ( rslt != 0 || ! ProtocolConfirmed() ) return;
|
||||
|
||||
Val* teredo_hdr = 0;
|
||||
|
||||
if ( teredo_packet )
|
||||
{
|
||||
teredo_hdr = te.BuildVal(inner);
|
||||
Conn()->Event(teredo_packet, 0, teredo_hdr);
|
||||
}
|
||||
|
||||
if ( te.Authentication() && teredo_authentication )
|
||||
{
|
||||
teredo_hdr = teredo_hdr ? teredo_hdr->Ref() : te.BuildVal(inner);
|
||||
Conn()->Event(teredo_authentication, 0, teredo_hdr);
|
||||
}
|
||||
|
||||
if ( te.OriginIndication() && teredo_origin_indication )
|
||||
{
|
||||
teredo_hdr = teredo_hdr ? teredo_hdr->Ref() : te.BuildVal(inner);
|
||||
Conn()->Event(teredo_origin_indication, 0, teredo_hdr);
|
||||
}
|
||||
|
||||
if ( inner->NextProto() == IPPROTO_NONE && teredo_bubble )
|
||||
{
|
||||
teredo_hdr = teredo_hdr ? teredo_hdr->Ref() : te.BuildVal(inner);
|
||||
Conn()->Event(teredo_bubble, 0, teredo_hdr);
|
||||
}
|
||||
|
||||
EncapsulatingConn ec(Conn(), BifEnum::Tunnel::TEREDO);
|
||||
|
||||
sessions->DoNextInnerPacket(network_time, 0, inner, e, ec);
|
||||
}
|
79
src/Teredo.h
Normal file
79
src/Teredo.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
#ifndef Teredo_h
|
||||
#define Teredo_h
|
||||
|
||||
#include "Analyzer.h"
|
||||
#include "NetVar.h"
|
||||
|
||||
class Teredo_Analyzer : public Analyzer {
|
||||
public:
|
||||
Teredo_Analyzer(Connection* conn) : Analyzer(AnalyzerTag::Teredo, conn)
|
||||
{}
|
||||
|
||||
virtual ~Teredo_Analyzer()
|
||||
{}
|
||||
|
||||
virtual void Done();
|
||||
|
||||
virtual void DeliverPacket(int len, const u_char* data, bool orig,
|
||||
int seq, const IP_Hdr* ip, int caplen);
|
||||
|
||||
static Analyzer* InstantiateAnalyzer(Connection* conn)
|
||||
{ return new Teredo_Analyzer(conn); }
|
||||
|
||||
static bool Available()
|
||||
{ return BifConst::Tunnel::enable_teredo &&
|
||||
BifConst::Tunnel::max_depth > 0; }
|
||||
|
||||
/**
|
||||
* Emits a weird only if the analyzer has previously been able to
|
||||
* decapsulate a Teredo packet since otherwise the weirds could happen
|
||||
* frequently enough to be less than helpful.
|
||||
*/
|
||||
void Weird(const char* name) const
|
||||
{
|
||||
if ( ProtocolConfirmed() )
|
||||
reporter->Weird(Conn(), name);
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class AnalyzerTimer;
|
||||
void ExpireTimer(double t);
|
||||
};
|
||||
|
||||
class TeredoEncapsulation {
|
||||
public:
|
||||
TeredoEncapsulation(const Teredo_Analyzer* ta)
|
||||
: inner_ip(0), origin_indication(0), auth(0), analyzer(ta)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Returns whether input data parsed as a valid Teredo encapsulation type.
|
||||
* If it was valid, the len argument is decremented appropriately.
|
||||
*/
|
||||
bool Parse(const u_char* data, int& len)
|
||||
{ return DoParse(data, len, false, false); }
|
||||
|
||||
const u_char* InnerIP() const
|
||||
{ return inner_ip; }
|
||||
|
||||
const u_char* OriginIndication() const
|
||||
{ return origin_indication; }
|
||||
|
||||
const u_char* Authentication() const
|
||||
{ return auth; }
|
||||
|
||||
RecordVal* BuildVal(const IP_Hdr* inner) const;
|
||||
|
||||
protected:
|
||||
bool DoParse(const u_char* data, int& len, bool found_orig, bool found_au);
|
||||
|
||||
void Weird(const char* name) const
|
||||
{ analyzer->Weird(name); }
|
||||
|
||||
const u_char* inner_ip;
|
||||
const u_char* origin_indication;
|
||||
const u_char* auth;
|
||||
const Teredo_Analyzer* analyzer;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -20,6 +20,7 @@ const char* TimerNames[] = {
|
|||
"IncrementalSendTimer",
|
||||
"IncrementalWriteTimer",
|
||||
"InterconnTimer",
|
||||
"IPTunnelInactivityTimer",
|
||||
"NetbiosExpireTimer",
|
||||
"NetworkTimer",
|
||||
"NTPExpireTimer",
|
||||
|
|
|
@ -26,6 +26,7 @@ enum TimerType {
|
|||
TIMER_INCREMENTAL_SEND,
|
||||
TIMER_INCREMENTAL_WRITE,
|
||||
TIMER_INTERCONN,
|
||||
TIMER_IP_TUNNEL_INACTIVITY,
|
||||
TIMER_NB_EXPIRE,
|
||||
TIMER_NETWORK,
|
||||
TIMER_NTP_EXPIRE,
|
||||
|
|
55
src/TunnelEncapsulation.cc
Normal file
55
src/TunnelEncapsulation.cc
Normal file
|
@ -0,0 +1,55 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include "TunnelEncapsulation.h"
|
||||
#include "util.h"
|
||||
#include "Conn.h"
|
||||
|
||||
EncapsulatingConn::EncapsulatingConn(Connection* c, BifEnum::Tunnel::Type t)
|
||||
: src_addr(c->OrigAddr()), dst_addr(c->RespAddr()),
|
||||
src_port(c->OrigPort()), dst_port(c->RespPort()),
|
||||
proto(c->ConnTransport()), type(t), uid(c->GetUID())
|
||||
{
|
||||
if ( ! uid )
|
||||
{
|
||||
uid = calculate_unique_id();
|
||||
c->SetUID(uid);
|
||||
}
|
||||
}
|
||||
|
||||
RecordVal* EncapsulatingConn::GetRecordVal() const
|
||||
{
|
||||
RecordVal *rv = new RecordVal(BifType::Record::Tunnel::EncapsulatingConn);
|
||||
|
||||
RecordVal* id_val = new RecordVal(conn_id);
|
||||
id_val->Assign(0, new AddrVal(src_addr));
|
||||
id_val->Assign(1, new PortVal(ntohs(src_port), proto));
|
||||
id_val->Assign(2, new AddrVal(dst_addr));
|
||||
id_val->Assign(3, new PortVal(ntohs(dst_port), proto));
|
||||
rv->Assign(0, id_val);
|
||||
rv->Assign(1, new EnumVal(type, BifType::Enum::Tunnel::Type));
|
||||
|
||||
char tmp[20];
|
||||
rv->Assign(2, new StringVal(uitoa_n(uid, tmp, sizeof(tmp), 62)));
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool operator==(const EncapsulationStack& e1, const EncapsulationStack& e2)
|
||||
{
|
||||
if ( ! e1.conns )
|
||||
return e2.conns;
|
||||
|
||||
if ( ! e2.conns )
|
||||
return false;
|
||||
|
||||
if ( e1.conns->size() != e2.conns->size() )
|
||||
return false;
|
||||
|
||||
for ( size_t i = 0; i < e1.conns->size(); ++i )
|
||||
{
|
||||
if ( (*e1.conns)[i] != (*e2.conns)[i] )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
208
src/TunnelEncapsulation.h
Normal file
208
src/TunnelEncapsulation.h
Normal file
|
@ -0,0 +1,208 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef TUNNELS_H
|
||||
#define TUNNELS_H
|
||||
|
||||
#include "config.h"
|
||||
#include "NetVar.h"
|
||||
#include "IPAddr.h"
|
||||
#include "Val.h"
|
||||
#include <vector>
|
||||
|
||||
class Connection;
|
||||
|
||||
/**
|
||||
* Represents various types of tunnel "connections", that is, a pair of
|
||||
* endpoints whose communication encapsulates inner IP packets. This could
|
||||
* mean IP packets nested inside IP packets or IP packets nested inside a
|
||||
* transport layer protocol. EncapsulatingConn's are assigned a UID, which can
|
||||
* be shared with Connection's in the case the tunnel uses a transport-layer.
|
||||
*/
|
||||
class EncapsulatingConn {
|
||||
public:
|
||||
/**
|
||||
* Default tunnel connection constructor.
|
||||
*/
|
||||
EncapsulatingConn()
|
||||
: src_port(0), dst_port(0), proto(TRANSPORT_UNKNOWN),
|
||||
type(BifEnum::Tunnel::NONE), uid(0)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Construct an IP tunnel "connection" with its own UID.
|
||||
* The assignment of "source" and "destination" addresses here can be
|
||||
* arbitrary, comparison between EncapsulatingConn objects will treat IP
|
||||
* tunnels as equivalent as long as the same two endpoints are involved.
|
||||
*
|
||||
* @param s The tunnel source address, likely taken from an IP header.
|
||||
* @param d The tunnel destination address, likely taken from an IP header.
|
||||
*/
|
||||
EncapsulatingConn(const IPAddr& s, const IPAddr& d)
|
||||
: src_addr(s), dst_addr(d), src_port(0), dst_port(0),
|
||||
proto(TRANSPORT_UNKNOWN), type(BifEnum::Tunnel::IP)
|
||||
{
|
||||
uid = calculate_unique_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a tunnel connection using information from an already existing
|
||||
* transport-layer-aware connection object.
|
||||
*
|
||||
* @param c The connection from which endpoint information can be extracted.
|
||||
* If it already has a UID associated with it, that gets inherited,
|
||||
* otherwise a new UID is created for this tunnel and \a c.
|
||||
* @param t The type of tunneling that is occurring over the connection.
|
||||
*/
|
||||
EncapsulatingConn(Connection* c, BifEnum::Tunnel::Type t);
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*/
|
||||
EncapsulatingConn(const EncapsulatingConn& other)
|
||||
: src_addr(other.src_addr), dst_addr(other.dst_addr),
|
||||
src_port(other.src_port), dst_port(other.dst_port),
|
||||
proto(other.proto), type(other.type), uid(other.uid)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~EncapsulatingConn()
|
||||
{}
|
||||
|
||||
BifEnum::Tunnel::Type Type() const
|
||||
{ return type; }
|
||||
|
||||
/**
|
||||
* Returns record value of type "EncapsulatingConn" representing the tunnel.
|
||||
*/
|
||||
RecordVal* GetRecordVal() const;
|
||||
|
||||
friend bool operator==(const EncapsulatingConn& ec1,
|
||||
const EncapsulatingConn& ec2)
|
||||
{
|
||||
if ( ec1.type != ec2.type )
|
||||
return false;
|
||||
|
||||
if ( ec1.type == BifEnum::Tunnel::IP )
|
||||
// Reversing endpoints is still same tunnel.
|
||||
return ec1.uid == ec2.uid && ec1.proto == ec2.proto &&
|
||||
((ec1.src_addr == ec2.src_addr && ec1.dst_addr == ec2.dst_addr) ||
|
||||
(ec1.src_addr == ec2.dst_addr && ec1.dst_addr == ec2.src_addr));
|
||||
|
||||
return ec1.src_addr == ec2.src_addr && ec1.dst_addr == ec2.dst_addr &&
|
||||
ec1.src_port == ec2.src_port && ec1.dst_port == ec2.dst_port &&
|
||||
ec1.uid == ec2.uid && ec1.proto == ec2.proto;
|
||||
}
|
||||
|
||||
friend bool operator!=(const EncapsulatingConn& ec1,
|
||||
const EncapsulatingConn& ec2)
|
||||
{
|
||||
return ! ( ec1 == ec2 );
|
||||
}
|
||||
|
||||
protected:
|
||||
IPAddr src_addr;
|
||||
IPAddr dst_addr;
|
||||
uint16 src_port;
|
||||
uint16 dst_port;
|
||||
TransportProto proto;
|
||||
BifEnum::Tunnel::Type type;
|
||||
uint64 uid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstracts an arbitrary amount of nested tunneling.
|
||||
*/
|
||||
class EncapsulationStack {
|
||||
public:
|
||||
EncapsulationStack() : conns(0)
|
||||
{}
|
||||
|
||||
EncapsulationStack(const EncapsulationStack& other)
|
||||
{
|
||||
if ( other.conns )
|
||||
conns = new vector<EncapsulatingConn>(*(other.conns));
|
||||
else
|
||||
conns = 0;
|
||||
}
|
||||
|
||||
EncapsulationStack& operator=(const EncapsulationStack& other)
|
||||
{
|
||||
if ( this == &other )
|
||||
return *this;
|
||||
|
||||
delete conns;
|
||||
|
||||
if ( other.conns )
|
||||
conns = new vector<EncapsulatingConn>(*(other.conns));
|
||||
else
|
||||
conns = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~EncapsulationStack() { delete conns; }
|
||||
|
||||
/**
|
||||
* Add a new inner-most tunnel to the EncapsulationStack.
|
||||
*
|
||||
* @param c The new inner-most tunnel to append to the tunnel chain.
|
||||
*/
|
||||
void Add(const EncapsulatingConn& c)
|
||||
{
|
||||
if ( ! conns )
|
||||
conns = new vector<EncapsulatingConn>();
|
||||
|
||||
conns->push_back(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return how many nested tunnels are involved in a encapsulation, zero
|
||||
* meaning no tunnels are present.
|
||||
*/
|
||||
size_t Depth() const
|
||||
{
|
||||
return conns ? conns->size() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tunnel type of the inner-most tunnel.
|
||||
*/
|
||||
BifEnum::Tunnel::Type LastType() const
|
||||
{
|
||||
return conns ? (*conns)[conns->size()-1].Type() : BifEnum::Tunnel::NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of type "EncapsulatingConnVector" represented by the
|
||||
* entire encapsulation chain.
|
||||
*/
|
||||
VectorVal* GetVectorVal() const
|
||||
{
|
||||
VectorVal* vv = new VectorVal(
|
||||
internal_type("EncapsulatingConnVector")->AsVectorType());
|
||||
|
||||
if ( conns )
|
||||
{
|
||||
for ( size_t i = 0; i < conns->size(); ++i )
|
||||
vv->Assign(i, (*conns)[i].GetRecordVal(), 0);
|
||||
}
|
||||
|
||||
return vv;
|
||||
}
|
||||
|
||||
friend bool operator==(const EncapsulationStack& e1,
|
||||
const EncapsulationStack& e2);
|
||||
|
||||
friend bool operator!=(const EncapsulationStack& e1,
|
||||
const EncapsulationStack& e2)
|
||||
{
|
||||
return ! ( e1 == e2 );
|
||||
}
|
||||
|
||||
protected:
|
||||
vector<EncapsulatingConn>* conns;
|
||||
};
|
||||
|
||||
#endif
|
86
src/ayiya-analyzer.pac
Normal file
86
src/ayiya-analyzer.pac
Normal file
|
@ -0,0 +1,86 @@
|
|||
|
||||
connection AYIYA_Conn(bro_analyzer: BroAnalyzer)
|
||||
{
|
||||
upflow = AYIYA_Flow;
|
||||
downflow = AYIYA_Flow;
|
||||
};
|
||||
|
||||
flow AYIYA_Flow
|
||||
{
|
||||
datagram = PDU withcontext(connection, this);
|
||||
|
||||
function process_ayiya(pdu: PDU): bool
|
||||
%{
|
||||
Connection *c = connection()->bro_analyzer()->Conn();
|
||||
const EncapsulationStack* e = c->GetEncapsulation();
|
||||
|
||||
if ( e && e->Depth() >= BifConst::Tunnel::max_depth )
|
||||
{
|
||||
reporter->Weird(c, "tunnel_depth");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ${pdu.op} != 1 )
|
||||
{
|
||||
// 1 is the "forward" command.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ${pdu.next_header} != IPPROTO_IPV6 &&
|
||||
${pdu.next_header} != IPPROTO_IPV4 )
|
||||
{
|
||||
reporter->Weird(c, "ayiya_tunnel_non_ip");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ${pdu.packet}.length() < (int)sizeof(struct ip) )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
"Truncated AYIYA", (const char*) ${pdu.packet}.data(),
|
||||
${pdu.packet}.length());
|
||||
return false;
|
||||
}
|
||||
|
||||
const struct ip* ip = (const struct ip*) ${pdu.packet}.data();
|
||||
|
||||
if ( ( ${pdu.next_header} == IPPROTO_IPV6 && ip->ip_v != 6 ) ||
|
||||
( ${pdu.next_header} == IPPROTO_IPV4 && ip->ip_v != 4) )
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
"AYIYA next header mismatch", (const char*)${pdu.packet}.data(),
|
||||
${pdu.packet}.length());
|
||||
return false;
|
||||
}
|
||||
|
||||
IP_Hdr* inner = 0;
|
||||
int result = sessions->ParseIPPacket(${pdu.packet}.length(),
|
||||
${pdu.packet}.data(), ${pdu.next_header}, inner);
|
||||
|
||||
if ( result == 0 )
|
||||
connection()->bro_analyzer()->ProtocolConfirmation();
|
||||
|
||||
else if ( result < 0 )
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
"Truncated AYIYA", (const char*) ${pdu.packet}.data(),
|
||||
${pdu.packet}.length());
|
||||
|
||||
else
|
||||
connection()->bro_analyzer()->ProtocolViolation(
|
||||
"AYIYA payload length", (const char*) ${pdu.packet}.data(),
|
||||
${pdu.packet}.length());
|
||||
|
||||
if ( result != 0 )
|
||||
return false;
|
||||
|
||||
EncapsulatingConn ec(c, BifEnum::Tunnel::AYIYA);
|
||||
|
||||
sessions->DoNextInnerPacket(network_time(), 0, inner, e, ec);
|
||||
|
||||
return (result == 0) ? true : false;
|
||||
%}
|
||||
|
||||
};
|
||||
|
||||
refine typeattr PDU += &let {
|
||||
proc_ayiya = $context.flow.process_ayiya(this);
|
||||
};
|
16
src/ayiya-protocol.pac
Normal file
16
src/ayiya-protocol.pac
Normal file
|
@ -0,0 +1,16 @@
|
|||
|
||||
type PDU = record {
|
||||
identity_byte: uint8;
|
||||
signature_byte: uint8;
|
||||
auth_and_op: uint8;
|
||||
next_header: uint8;
|
||||
epoch: uint32;
|
||||
identity: bytestring &length=identity_len;
|
||||
signature: bytestring &length=signature_len;
|
||||
packet: bytestring &restofdata;
|
||||
} &let {
|
||||
identity_len = (1 << (identity_byte >> 4));
|
||||
signature_len = (signature_byte >> 4) * 4;
|
||||
auth = auth_and_op >> 4;
|
||||
op = auth_and_op & 0xF;
|
||||
} &byteorder = littleendian;
|
10
src/ayiya.pac
Normal file
10
src/ayiya.pac
Normal file
|
@ -0,0 +1,10 @@
|
|||
%include binpac.pac
|
||||
%include bro.pac
|
||||
|
||||
analyzer AYIYA withcontext {
|
||||
connection: AYIYA_Conn;
|
||||
flow: AYIYA_Flow;
|
||||
};
|
||||
|
||||
%include ayiya-protocol.pac
|
||||
%include ayiya-analyzer.pac
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
const ignore_keep_alive_rexmit: bool;
|
||||
const skip_http_data: bool;
|
||||
const parse_udp_tunnels: bool;
|
||||
const use_conn_size_analyzer: bool;
|
||||
const report_gaps_for_partial: bool;
|
||||
|
||||
|
@ -12,4 +11,11 @@ const NFS3::return_data: bool;
|
|||
const NFS3::return_data_max: count;
|
||||
const NFS3::return_data_first_only: bool;
|
||||
|
||||
const Tunnel::max_depth: count;
|
||||
const Tunnel::enable_ip: bool;
|
||||
const Tunnel::enable_ayiya: bool;
|
||||
const Tunnel::enable_teredo: bool;
|
||||
const Tunnel::yielding_teredo_decapsulation: bool;
|
||||
const Tunnel::ip_tunnel_timeout: interval;
|
||||
|
||||
const Threading::heartbeat_interval: interval;
|
||||
|
|
105
src/event.bif
105
src/event.bif
|
@ -143,7 +143,21 @@ event dns_mapping_altered%(dm: dns_mapping, old_addrs: addr_set, new_addrs: addr
|
|||
## event.
|
||||
event new_connection%(c: connection%);
|
||||
|
||||
## Generated when reassembly starts for a TCP connection. This event is raised
|
||||
## Generated for a connection whose tunneling has changed. This could
|
||||
## be from a previously seen connection now being encapsulated in a tunnel,
|
||||
## or from the outer encapsulation changing. Note that connection *c*'s
|
||||
## *tunnel* field is NOT automatically/internally assigned to the new
|
||||
## encapsulation value of *e* after this event is raised. If the desired
|
||||
## behavior is to track the latest tunnel encapsulation per-connection,
|
||||
## then a handler of this event should assign *e* to ``c$tunnel`` (which Bro's
|
||||
## default scripts are doing).
|
||||
##
|
||||
## c: The connection whose tunnel/encapsulation changed.
|
||||
##
|
||||
## e: The new encapsulation.
|
||||
event tunnel_changed%(c: connection, e: EncapsulatingConnVector%);
|
||||
|
||||
## Generated when reassembly starts for a TCP connection. The event is raised
|
||||
## at the moment when Bro's TCP analyzer enables stream reassembly for a
|
||||
## connection.
|
||||
##
|
||||
|
@ -508,12 +522,76 @@ event esp_packet%(p: pkt_hdr%);
|
|||
## .. bro:see:: new_packet tcp_packet ipv6_ext_headers
|
||||
event mobile_ipv6_message%(p: pkt_hdr%);
|
||||
|
||||
<<<<<<< HEAD
|
||||
## Generated for every packet that has a non-empty transport-layer payload.
|
||||
## This is a very low-level and expensive event that should be avoided when
|
||||
## at all possible. It's usually infeasible to handle when processing even
|
||||
## medium volumes of traffic in real-time. It's even worse than
|
||||
## :bro:id:`new_packet`. That said, if you work from a trace and want to
|
||||
## do some packet-level analysis, it may come in handy.
|
||||
=======
|
||||
## Genereated for any IPv6 packet encapsulated in a Teredo tunnel.
|
||||
## See :rfc:`4380` for more information about the Teredo protocol.
|
||||
##
|
||||
## outer: The Teredo tunnel connection.
|
||||
##
|
||||
## inner: The Teredo-encapsulated IPv6 packet header and transport header.
|
||||
##
|
||||
## .. bro:see:: teredo_authentication teredo_origin_indication teredo_bubble
|
||||
##
|
||||
## .. note:: Since this event may be raised on a per-packet basis, handling
|
||||
## it may become particular expensive for real-time analysis.
|
||||
event teredo_packet%(outer: connection, inner: teredo_hdr%);
|
||||
|
||||
## Genereated for IPv6 packets encapsulated in a Teredo tunnel that
|
||||
## use the Teredo authentication encapsulation method.
|
||||
## See :rfc:`4380` for more information about the Teredo protocol.
|
||||
##
|
||||
## outer: The Teredo tunnel connection.
|
||||
##
|
||||
## inner: The Teredo-encapsulated IPv6 packet header and transport header.
|
||||
##
|
||||
## .. bro:see:: teredo_packet teredo_origin_indication teredo_bubble
|
||||
##
|
||||
## .. note:: Since this event may be raised on a per-packet basis, handling
|
||||
## it may become particular expensive for real-time analysis.
|
||||
event teredo_authentication%(outer: connection, inner: teredo_hdr%);
|
||||
|
||||
## Genereated for IPv6 packets encapsulated in a Teredo tunnel that
|
||||
## use the Teredo origin indication encapsulation method.
|
||||
## See :rfc:`4380` for more information about the Teredo protocol.
|
||||
##
|
||||
## outer: The Teredo tunnel connection.
|
||||
##
|
||||
## inner: The Teredo-encapsulated IPv6 packet header and transport header.
|
||||
##
|
||||
## .. bro:see:: teredo_packet teredo_authentication teredo_bubble
|
||||
##
|
||||
## .. note:: Since this event may be raised on a per-packet basis, handling
|
||||
## it may become particular expensive for real-time analysis.
|
||||
event teredo_origin_indication%(outer: connection, inner: teredo_hdr%);
|
||||
|
||||
## Genereated for Teredo bubble packets. That is, IPv6 packets encapsulated
|
||||
## in a Teredo tunnel that have a Next Header value of :bro:id:`IPPROTO_NONE`.
|
||||
## See :rfc:`4380` for more information about the Teredo protocol.
|
||||
##
|
||||
## outer: The Teredo tunnel connection.
|
||||
##
|
||||
## inner: The Teredo-encapsulated IPv6 packet header and transport header.
|
||||
##
|
||||
## .. bro:see:: teredo_packet teredo_authentication teredo_origin_indication
|
||||
##
|
||||
## .. note:: Since this event may be raised on a per-packet basis, handling
|
||||
## it may become particular expensive for real-time analysis.
|
||||
event teredo_bubble%(outer: connection, inner: teredo_hdr%);
|
||||
|
||||
## Generated for every packet that has non-empty transport-layer payload. This is a
|
||||
## very low-level and expensive event that should be avoided when at all possible.
|
||||
## It's usually infeasible to handle when processing even medium volumes of
|
||||
## traffic in real-time. It's even worse than :bro:id:`new_packet`. That said, if
|
||||
## you work from a trace and want to do some packet-level analysis, it may come in
|
||||
## handy.
|
||||
>>>>>>> topic/robin/tunnels-merge
|
||||
##
|
||||
## c: The connection the packet is part of.
|
||||
##
|
||||
|
@ -788,8 +866,13 @@ event udp_contents%(u: connection, is_orig: bool, contents: string%);
|
|||
|
||||
## Generated when a UDP session for a supported protocol has finished. Some of
|
||||
## Bro's application-layer UDP analyzers flag the end of a session by raising
|
||||
<<<<<<< HEAD
|
||||
## this event. Currently, the analyzers for DNS, NTP, Netbios, and Syslog
|
||||
## support this.
|
||||
=======
|
||||
## this event. Currently, the analyzers for DNS, NTP, Netbios, Syslog, AYIYA,
|
||||
## and Teredo support this.
|
||||
>>>>>>> topic/robin/tunnels-merge
|
||||
##
|
||||
## u: The connection record for the corresponding UDP flow.
|
||||
##
|
||||
|
@ -6139,6 +6222,26 @@ event syslog_message%(c: connection, facility: count, severity: count, msg: stri
|
|||
## triggering the match will be passed on to the event.
|
||||
event signature_match%(state: signature_state, msg: string, data: string%);
|
||||
|
||||
## Generated when a SOCKS request is analyzed.
|
||||
##
|
||||
## c: The parent connection of the proxy.
|
||||
##
|
||||
## t: The type of the request.
|
||||
##
|
||||
## dstaddr: Address that the tunneled traffic should be sent to.
|
||||
##
|
||||
## dstname: DNS name of the host that the tunneled traffic should be sent to.
|
||||
##
|
||||
## p: The destination port for the proxied traffic.
|
||||
##
|
||||
## user: Username given for the SOCKS connection.
|
||||
event socks_request%(c: connection, request_type: count, dstaddr: addr, dstname: string, p: port, user: string%);
|
||||
|
||||
## Generated when a SOCKS reply is analyzed.
|
||||
##
|
||||
##
|
||||
event socks_reply%(c: connection, granted: bool, dst: addr, p: port%);
|
||||
|
||||
## Generated when a protocol analyzer finds an identification of a software
|
||||
## used on a system. This is a protocol-independent event that is fed by
|
||||
## different analyzers. For example, the HTTP analyzer reports user-agent and
|
||||
|
|
57
src/socks-analyzer.pac
Normal file
57
src/socks-analyzer.pac
Normal file
|
@ -0,0 +1,57 @@
|
|||
|
||||
%header{
|
||||
StringVal* array_to_string(vector<uint8> *a);
|
||||
%}
|
||||
|
||||
%code{
|
||||
StringVal* array_to_string(vector<uint8> *a)
|
||||
{
|
||||
int len = a->size();
|
||||
char tmp[len];
|
||||
char *s = tmp;
|
||||
for ( vector<uint8>::iterator i = a->begin(); i != a->end(); *s++ = *i++ );
|
||||
|
||||
while ( len > 0 && tmp[len-1] == '\0' )
|
||||
--len;
|
||||
|
||||
return new StringVal(len, tmp);
|
||||
}
|
||||
%}
|
||||
|
||||
refine connection SOCKS_Conn += {
|
||||
function socks_request(cmd: uint8, dstaddr: uint32, dstname: uint8[], p: uint16, user: uint8[]): bool
|
||||
%{
|
||||
BifEvent::generate_socks_request(bro_analyzer(),
|
||||
bro_analyzer()->Conn(),
|
||||
cmd,
|
||||
new AddrVal(htonl(dstaddr)),
|
||||
array_to_string(dstname),
|
||||
new PortVal(p | TCP_PORT_MASK),
|
||||
array_to_string(user));
|
||||
|
||||
static_cast<SOCKS_Analyzer*>(bro_analyzer())->EndpointDone(true);
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
function socks_reply(granted: bool, dst: uint32, p: uint16): bool
|
||||
%{
|
||||
BifEvent::generate_socks_reply(bro_analyzer(),
|
||||
bro_analyzer()->Conn(),
|
||||
granted,
|
||||
new AddrVal(htonl(dst)),
|
||||
new PortVal(p | TCP_PORT_MASK));
|
||||
|
||||
bro_analyzer()->ProtocolConfirmation();
|
||||
static_cast<SOCKS_Analyzer*>(bro_analyzer())->EndpointDone(false);
|
||||
return true;
|
||||
%}
|
||||
};
|
||||
|
||||
refine typeattr SOCKS_Request += &let {
|
||||
proc: bool = $context.connection.socks_request(command, addr, empty, port, user);
|
||||
};
|
||||
|
||||
refine typeattr SOCKS_Reply += &let {
|
||||
proc: bool = $context.connection.socks_reply((status == 0x5a), addr, port);
|
||||
};
|
34
src/socks-protocol.pac
Normal file
34
src/socks-protocol.pac
Normal file
|
@ -0,0 +1,34 @@
|
|||
type SOCKS_Message(is_orig: bool) = case is_orig of {
|
||||
true -> request: SOCKS_Request;
|
||||
false -> reply: SOCKS_Reply;
|
||||
};
|
||||
|
||||
type SOCKS_Request = record {
|
||||
version: uint8;
|
||||
command: uint8;
|
||||
port: uint16;
|
||||
addr: uint32;
|
||||
user: uint8[] &until($element == 0);
|
||||
|
||||
host: case v4a of {
|
||||
true -> name: uint8[] &until($element == 0); # v4a
|
||||
false -> empty: uint8[] &length=0;
|
||||
} &requires(v4a);
|
||||
|
||||
# FIXME: Can this be non-zero? If so we need to keep it for the
|
||||
# next analyzer.
|
||||
rest: bytestring &restofdata;
|
||||
} &byteorder = bigendian &let {
|
||||
v4a: bool = (addr <= 0x000000ff);
|
||||
};
|
||||
|
||||
type SOCKS_Reply = record {
|
||||
zero: uint8;
|
||||
status: uint8;
|
||||
port: uint16;
|
||||
addr: uint32;
|
||||
|
||||
# FIXME: Can this be non-zero? If so we need to keep it for the
|
||||
# next analyzer.
|
||||
rest: bytestring &restofdata;
|
||||
} &byteorder = bigendian;
|
24
src/socks.pac
Normal file
24
src/socks.pac
Normal file
|
@ -0,0 +1,24 @@
|
|||
%include binpac.pac
|
||||
%include bro.pac
|
||||
|
||||
%extern{
|
||||
#include "SOCKS.h"
|
||||
%}
|
||||
|
||||
analyzer SOCKS withcontext {
|
||||
connection: SOCKS_Conn;
|
||||
flow: SOCKS_Flow;
|
||||
};
|
||||
|
||||
connection SOCKS_Conn(bro_analyzer: BroAnalyzer) {
|
||||
upflow = SOCKS_Flow(true);
|
||||
downflow = SOCKS_Flow(false);
|
||||
};
|
||||
|
||||
%include socks-protocol.pac
|
||||
|
||||
flow SOCKS_Flow(is_orig: bool) {
|
||||
datagram = SOCKS_Message(is_orig) withcontext(connection, this);
|
||||
};
|
||||
|
||||
%include socks-analyzer.pac
|
|
@ -169,6 +169,17 @@ enum ID %{
|
|||
Unknown,
|
||||
%}
|
||||
|
||||
module Tunnel;
|
||||
enum Type %{
|
||||
NONE,
|
||||
IP,
|
||||
AYIYA,
|
||||
TEREDO,
|
||||
SOCKS,
|
||||
%}
|
||||
|
||||
type EncapsulatingConn: record;
|
||||
|
||||
module Input;
|
||||
|
||||
enum Reader %{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue