Adding support to de-capsulate tunnels.

Checkpoint.
Decapsulation happens after IP Defragmentation. The "identity" of the
enclosing tunnel (the "parent") is added to the connection record of the
child (tunneled) connection as an optional field $tunnel_parent.
This commit is contained in:
Gregor Maier 2011-08-03 14:12:47 -07:00
parent 6c806b0bce
commit 9c388a1809
10 changed files with 183 additions and 17 deletions

View file

@ -81,6 +81,11 @@ type endpoint_stats: record {
type AnalyzerID: count;
type tunnel_parent_t: record {
cid: conn_id;
tunnel_type: tunneltype_t;
};
type connection: record {
id: conn_id;
orig: endpoint;
@ -92,6 +97,7 @@ type connection: record {
hot: count; # how hot; 0 = don't know or not hot
history: string;
uid: string;
tunnel_parent: tunnel_parent_t &optional;
};
type SYN_packet: record {

View file

@ -388,6 +388,7 @@ set(bro_SRCS
Timer.cc
Traverse.cc
Trigger.cc
TunnelHandler.cc
Type.cc
UDP.cc
Val.cc

View file

@ -139,7 +139,7 @@ unsigned int Connection::external_connections = 0;
IMPLEMENT_SERIAL(Connection, SER_CONNECTION);
Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id)
Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id, RecordVal *arg_tunnel_parent)
{
sessions = s;
key = k;
@ -183,6 +183,8 @@ Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id)
uid = 0; // Will set later.
tunnel_parent = arg_tunnel_parent;
if ( conn_timer_mgr )
{
++external_connections;
@ -370,6 +372,7 @@ RecordVal* Connection::BuildConnVal()
char tmp[20];
conn_val->Assign(9, new StringVal(uitoa_n(uid, tmp, sizeof(tmp), 62)));
conn_val->Assign(10, tunnel_parent);
}
if ( root_analyzer )

View file

@ -86,7 +86,7 @@ class Analyzer;
class Connection : public BroObj {
public:
Connection(NetSessions* s, HashKey* k, double t, const ConnID* id);
Connection(NetSessions* s, HashKey* k, double t, const ConnID* id, RecordVal *arg_tunnel_parent);
virtual ~Connection();
// Invoked when connection is about to be removed. Use Ref(this)
@ -335,6 +335,7 @@ protected:
double inactivity_timeout;
RecordVal* conn_val;
LoginConn* login_conn; // either nil, or this
RecordVal* tunnel_parent; // nil if not tunneled
int suppress_event; // suppress certain events to once per conn.
unsigned int installed_status_timer:1;

View file

@ -521,7 +521,7 @@ Connection* ConnCompressor::Instantiate(HashKey* key, PendingConn* pending)
// Fake the first packet.
const IP_Hdr* faked_pkt = PendingConnToPacket(pending);
Connection* new_conn = sessions->NewConn(key, pending->time, &conn_id,
faked_pkt->Payload(), IPPROTO_TCP);
faked_pkt->Payload(), IPPROTO_TCP, 0);
if ( ! new_conn )
{
@ -574,7 +574,7 @@ Connection* ConnCompressor::Instantiate(double t, HashKey* key,
conn_id.dst_port = tp->th_dport;
Connection* new_conn =
sessions->NewConn(key, t, &conn_id, ip->Payload(), IPPROTO_TCP);
sessions->NewConn(key, t, &conn_id, ip->Payload(), IPPROTO_TCP, 0);
if ( ! new_conn )
{

View file

@ -33,6 +33,7 @@
#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
@ -128,6 +129,12 @@ NetSessions::NetSessions()
arp_analyzer = new ARP_Analyzer();
else
arp_analyzer = 0;
if ( 1 )
tunnel_handler = new TunnelHandler(this);
else
tunnel_handler = 0;
}
NetSessions::~NetSessions()
@ -433,14 +440,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
if ( discarder && discarder->NextPacket(ip_hdr, len, caplen) )
return;
int proto = ip_hdr->NextProto();
if ( proto != IPPROTO_TCP && proto != IPPROTO_UDP &&
proto != IPPROTO_ICMP )
{
dump_this_packet = 1;
return;
}
FragReassembler* f = 0;
uint32 frag_field = ip_hdr->FragField();
@ -474,6 +473,23 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
}
}
TunnelInfo *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;
return;
}
len -= ip_hdr_len; // remove IP header
caplen -= ip_hdr_len;
@ -561,7 +577,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);
conn = NewConn(h, t, &id, data, proto, tunnel_info);
if ( conn )
d->Insert(h, conn);
}
@ -581,7 +597,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
conn->Event(connection_reused, 0);
Remove(conn);
conn = NewConn(h, t, &id, data, proto);
conn = NewConn(h, t, &id, data, proto, tunnel_info);
if ( conn )
d->Insert(h, conn);
}
@ -609,6 +625,8 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
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.
@ -1045,13 +1063,17 @@ void NetSessions::GetStats(SessionStats& s) const
}
Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id,
const u_char* data, int proto)
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;
RecordVal *tunnel_parent = 0;
if ( tunnel_info )
tunnel_parent = tunnel_info->GetRecordVal();
// Hmm... This is not great.
TransportProto tproto;
@ -1098,7 +1120,7 @@ Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id,
id = &flip_id;
}
Connection* conn = new Connection(this, k, t, id);
Connection* conn = new Connection(this, k, t, id, tunnel_parent);
conn->SetTransport(tproto);
dpm->BuildInitialAnalyzerTree(tproto, conn, data);

View file

@ -28,6 +28,9 @@ class Discarder;
class SteppingStoneManager;
class PacketFilter;
class TunnelHandler;
class TunnelInfo;
class PacketSortElement;
struct SessionStats {
@ -144,7 +147,7 @@ protected:
friend class TimerMgrExpireTimer;
Connection* NewConn(HashKey* k, double t, const ConnID* id,
const u_char* data, int proto);
const u_char* data, int proto, TunnelInfo *tunnel_info);
// Check whether the tag of the current packet is consistent with
// the given connection. Returns:
@ -214,6 +217,8 @@ protected:
int num_packets_processed;
PacketProfiler* pkt_profiler;
TunnelHandler *tunnel_handler;
// We may use independent timer managers for different sets of related
// activity. The managers are identified by an unique tag.
typedef std::map<TimerMgr::Tag, TimerMgr*> TimerMgrMap;

48
src/TunnelHandler.cc Normal file
View file

@ -0,0 +1,48 @@
// $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 "TunnelHandler.h"
#include "Conn.h"
#include "Sessions.h"
TunnelHandler::TunnelHandler(NetSessions *arg_s)
{
s = arg_s;
}
TunnelInfo* TunnelHandler::DecapsulateTunnel(const IP_Hdr *ip_hdr, int len, int caplen,
const struct pcap_pkthdr* hdr, const u_char* const pkt)
{
TunnelInfo *tunnel_info = 0;
switch (ip_hdr->NextProto()) {
#ifdef BROv6
case IPPROTO_IPV6: /* 6in4 and 6to4 */
if (len < (int)sizeof(struct ip6_hdr) || caplen < (int)sizeof(struct ip6_hdr))
{
s->Weird("truncated_header", hdr, pkt);
return 0;
}
// TODO: check if IP6 header makes sense
tunnel_info = new TunnelInfo();
tunnel_info->child = new IP_Hdr((const struct ip6_hdr*)ip_hdr->Payload());
tunnel_info->tunneltype = BifEnum::IP6inIP;
tunnel_info->hdr_len = ip_hdr->HdrLen();
tunnel_info->SetParentIPs(ip_hdr);
return tunnel_info;
break;
#endif
default:
break;
} /* end switch */
return 0;
}

73
src/TunnelHandler.h Normal file
View file

@ -0,0 +1,73 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef tunnelhandler_h
#define tunnelhandler_h
#include "IP.h"
#include "Conn.h"
#include "Sessions.h"
#include "Val.h"
class TunnelInfo {
public:
TunnelInfo()
{
child = 0;
tunneltype = BifEnum::NONE;
hdr_len = 0;
parent.src_addr = parent.dst_addr = 0;
parent.src_port = parent.dst_port = 0;
parent.is_one_way = 0;
}
~TunnelInfo()
{
if (child) delete child;
}
void SetParentIPs(const IP_Hdr *ip_hdr)
{
parent.src_addr = ip_hdr->SrcAddr();
parent.dst_addr = ip_hdr->DstAddr();
}
void SetParentPorts(uint32 src_port, uint32 dst_port)
{
parent.src_port = src_port;
parent.dst_port = dst_port;
}
RecordVal* GetRecordVal() const
{
RecordVal *rv = new RecordVal(BifType::Record::tunnel_parent_t);
RecordVal* id_val = new RecordVal(conn_id);
id_val->Assign(0, new AddrVal(parent.src_addr));
id_val->Assign(1, new PortVal(ntohs(parent.src_port), TRANSPORT_UNKNOWN));
id_val->Assign(2, new AddrVal(parent.dst_addr));
id_val->Assign(3, new PortVal(ntohs(parent.dst_port), TRANSPORT_UNKNOWN));
rv->Assign(0, id_val);
rv->Assign(1, new EnumVal(tunneltype, BifType::Enum::tunneltype_t));
return rv;
}
IP_Hdr *child;
ConnID parent;
int hdr_len;
BifEnum::tunneltype_t tunneltype;
};
class TunnelHandler {
public:
TunnelHandler(NetSessions *arg_s);
~TunnelHandler();
TunnelInfo* DecapsulateTunnel(const IP_Hdr* ip_hdr, int len, int caplen,
/* need those for passing them back to NetSessions::Weird() */
const struct pcap_pkthdr* hdr, const u_char* const pkt);
protected:
NetSessions *s;
};
#endif

View file

@ -167,3 +167,10 @@ enum ID %{
%}
module GLOBAL;
enum tunneltype_t %{
NONE,
IP6inIP,
%}
type tunnel_parent_t: record;