From 9c388a18091d07e5adf5fa3b621c83fd46d5890a Mon Sep 17 00:00:00 2001 From: Gregor Maier Date: Wed, 3 Aug 2011 14:12:47 -0700 Subject: [PATCH] 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. --- policy/bro.init | 6 ++++ src/CMakeLists.txt | 1 + src/Conn.cc | 5 ++- src/Conn.h | 3 +- src/ConnCompressor.cc | 4 +-- src/Sessions.cc | 46 ++++++++++++++++++++------- src/Sessions.h | 7 ++++- src/TunnelHandler.cc | 48 ++++++++++++++++++++++++++++ src/TunnelHandler.h | 73 +++++++++++++++++++++++++++++++++++++++++++ src/types.bif | 7 +++++ 10 files changed, 183 insertions(+), 17 deletions(-) create mode 100644 src/TunnelHandler.cc create mode 100644 src/TunnelHandler.h diff --git a/policy/bro.init b/policy/bro.init index fda8cfd6f4..17607a7113 100644 --- a/policy/bro.init +++ b/policy/bro.init @@ -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 { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1a5f096f70..e79fad4ca0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -388,6 +388,7 @@ set(bro_SRCS Timer.cc Traverse.cc Trigger.cc + TunnelHandler.cc Type.cc UDP.cc Val.cc diff --git a/src/Conn.cc b/src/Conn.cc index bab032cbd0..67e337fda9 100644 --- a/src/Conn.cc +++ b/src/Conn.cc @@ -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 ) diff --git a/src/Conn.h b/src/Conn.h index 8f817fd003..e22c0b83ec 100644 --- a/src/Conn.h +++ b/src/Conn.h @@ -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; diff --git a/src/ConnCompressor.cc b/src/ConnCompressor.cc index e173463205..2d617b0fc4 100644 --- a/src/ConnCompressor.cc +++ b/src/ConnCompressor.cc @@ -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 ) { diff --git a/src/Sessions.cc b/src/Sessions.cc index 1678f6798f..48fab1bff4 100644 --- a/src/Sessions.cc +++ b/src/Sessions.cc @@ -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); diff --git a/src/Sessions.h b/src/Sessions.h index 6adc333282..9551ba5254 100644 --- a/src/Sessions.h +++ b/src/Sessions.h @@ -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 TimerMgrMap; diff --git a/src/TunnelHandler.cc b/src/TunnelHandler.cc new file mode 100644 index 0000000000..c739403ad8 --- /dev/null +++ b/src/TunnelHandler.cc @@ -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 + +#include +#include + +#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; + } diff --git a/src/TunnelHandler.h b/src/TunnelHandler.h new file mode 100644 index 0000000000..aa4cae0a39 --- /dev/null +++ b/src/TunnelHandler.h @@ -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 diff --git a/src/types.bif b/src/types.bif index 8bc5ab8510..d44f177b82 100644 --- a/src/types.bif +++ b/src/types.bif @@ -167,3 +167,10 @@ enum ID %{ %} module GLOBAL; + +enum tunneltype_t %{ + NONE, + IP6inIP, +%} + +type tunnel_parent_t: record;