Refactor IP-in-IP tunnel support.

UDP tunnel support removed for now, to be re-added in specific
analyzers later, but IP-in-IP is now decapsulated recursively
so nested tunnels can be seen and the inner packets get sent
through the IP fragment reassembler if necessary.
This commit is contained in:
Jon Siwek 2012-04-23 13:15:29 -05:00
parent 4062fc1776
commit b51dd191d7
21 changed files with 300 additions and 323 deletions

View file

@ -179,19 +179,27 @@ type AnalyzerID: count;
module Tunnel;
export {
## Records the identity of a the parent of a tunneled connection.
type Parent: record {
## The 4-tuple of the tunnel "connection". In case of an IP-in-IP
## Records the identity of an encapsulating parent of a tunneled connection.
type EncapsulatingConn: record {
## The 4-tuple of the encapsulating "connection". In case of an IP-in-IP
## tunnel the ports will be set to 0. The direction (i.e., orig and
## resp) of the parent are set according to the tunneled connection
## resp) are set according to the first tunneled packet seen
## and not according to the side that established the tunnel.
cid: conn_id;
## The type of tunnel.
tunnel_type: Tunneltype;
tunnel_type: Tunnel::Type;
} &log;
} # end export
module GLOBAL;
## A type alias for a vector of encapsulating "connections", i.e for when
## there are tunnels within tunnels.
##
## .. todo:: We need this type definition only for declaring builtin functions
## via ``bifcl``. We should extend ``bifcl`` to understand composite types
## directly and then remove this alias.
type encapsulating_conns: vector of Tunnel::EncapsulatingConn;
## Statistics about an endpoint.
##
## todo::Where is this used?
@ -239,8 +247,11 @@ type connection: record {
## used to tag and locate information associated with that connection.
uid: string;
## If the connection is tunneled, this field contains information about
## the encapsulating "connection".
tunnel_parent: Tunnel::Parent &optional;
## the encapsulating "connection(s)" with the outermost one starting
## at index zero. It's also always the first such enapsulation seen
## for the connection unless the :bro:id:`tunnel_changed` event is handled
## and re-assigns this field to the new encapsulation.
tunnel: encapsulating_conns &optional;
};
## Fields of a SYN packet.
@ -2616,16 +2627,10 @@ const record_all_packets = F &redef;
## .. bro:see:: conn_stats
const ignore_keep_alive_rexmit = F &redef;
## Whether the analysis engine parses IP packets encapsulated in
## UDP tunnels.
##
## .. bro:see:: tunnel_port
const parse_udp_tunnels = F &redef;
module Tunnel;
export {
## Whether to decapsulate IP tunnels (IPinIP, 6in4, 6to4)
const decapsulate_ip = F &redef;
const decapsulate_ip = T &redef;
## Whether to decapsulate UDP tunnels (e.g., Teredo, IPv4 in UDP)
const decapsulate_udp = F &redef;
@ -2640,6 +2645,9 @@ export {
## If udp_tunnel_allports is T :bro:id:`udp_tunnel_ports` is ignored and we
## check every UDP packet for tunnels.
const udp_tunnel_allports = F &redef;
## The maximum depth of a tunnel to decapsulate until giving up.
const max_depth: count = 2 &redef;
} # end export
module GLOBAL;

View file

@ -24,9 +24,8 @@
##! is stored as the ``tunnel_parent`` member of :bro:type:`connection`,
##! which is of type :bro:type:`Tunnel::Parent`.
##!
##! *Limitation:* The decapsulated packets are not fed through the
##! defragmenter again and decapsulation happens only on the primary
##! path, i.e., it's not available for the secondary path.
##! *Limitation:* decapsulation happens only on the primary path, i.e.
##! it's not available for the secondary path.
@load base/protocols/conn

View file

@ -112,7 +112,8 @@ unsigned int Connection::external_connections = 0;
IMPLEMENT_SERIAL(Connection, SER_CONNECTION);
Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id, TunnelParent* arg_tunnel_parent)
Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id,
const Encapsulation& arg_encap)
{
sessions = s;
key = k;
@ -156,7 +157,7 @@ Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id, T
uid = 0; // Will set later.
tunnel_parent = arg_tunnel_parent;
encapsulation = arg_encap;
if ( conn_timer_mgr )
{
@ -182,7 +183,6 @@ Connection::~Connection()
Unref(conn_val);
}
delete tunnel_parent;
delete key;
delete root_analyzer;
delete conn_timer_mgr;
@ -192,6 +192,15 @@ Connection::~Connection()
--external_connections;
}
void Connection::CheckEncapsulation(const Encapsulation& arg_encap)
{
if ( encapsulation != arg_encap )
{
Event(tunnel_changed, 0, arg_encap.GetVectorVal());
encapsulation = arg_encap;
}
}
void Connection::Done()
{
finished = 1;
@ -346,8 +355,8 @@ RecordVal* Connection::BuildConnVal()
char tmp[20];
conn_val->Assign(9, new StringVal(uitoa_n(uid, tmp, sizeof(tmp), 62)));
if ( tunnel_parent )
conn_val->Assign(10, tunnel_parent->GetRecordVal());
if ( encapsulation.Depth() > 0 )
conn_val->Assign(10, encapsulation.GetVectorVal());
}
if ( root_analyzer )

View file

@ -13,6 +13,7 @@
#include "RuleMatcher.h"
#include "AnalyzerTags.h"
#include "IPAddr.h"
#include "TunnelHandler.h"
class Connection;
class ConnectionTimer;
@ -51,9 +52,12 @@ class Analyzer;
class Connection : public BroObj {
public:
Connection(NetSessions* s, HashKey* k, double t, const ConnID* id, TunnelParent *arg_tunnel_parent);
Connection(NetSessions* s, HashKey* k, double t, const ConnID* id,
const Encapsulation& arg_encap);
virtual ~Connection();
void CheckEncapsulation(const Encapsulation& arg_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
@ -276,7 +280,7 @@ protected:
double inactivity_timeout;
RecordVal* conn_val;
LoginConn* login_conn; // either nil, or this
TunnelParent* tunnel_parent; // nil if not tunneled
Encapsulation encapsulation; // tunnels
int suppress_event; // suppress certain events to once per conn.
unsigned int installed_status_timer:1;

View file

@ -30,9 +30,6 @@ int partial_connection_ok;
int tcp_SYN_ack_ok;
int tcp_match_undelivered;
int encap_hdr_size;
int udp_tunnel_port;
double frag_timeout;
double tcp_SYN_timeout;
@ -322,10 +319,6 @@ void init_net_var()
tcp_SYN_ack_ok = opt_internal_int("tcp_SYN_ack_ok");
tcp_match_undelivered = opt_internal_int("tcp_match_undelivered");
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");

View file

@ -33,9 +33,6 @@ extern int partial_connection_ok;
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;
extern double tcp_SYN_timeout;

View file

@ -126,12 +126,6 @@ NetSessions::NetSessions()
arp_analyzer = new ARP_Analyzer();
else
arp_analyzer = 0;
if ( BifConst::Tunnel::decapsulate_ip || BifConst::Tunnel::decapsulate_udp )
tunnel_handler = new TunnelHandler(this);
else
tunnel_handler = 0;
}
NetSessions::~NetSessions()
@ -185,6 +179,8 @@ void NetSessions::NextPacket(double t, const struct pcap_pkthdr* hdr,
if ( record_all_packets )
DumpPacket(hdr, pkt);
Encapsulation encapsulation;
if ( pkt_elem && pkt_elem->IPHdr() )
// Fast path for "normal" IP packets if an IP_Hdr is
// already extracted when doing PacketSort. Otherwise
@ -192,7 +188,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, encapsulation);
else
{
@ -217,7 +213,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, encapsulation);
}
else if ( ip->ip_v == 6 )
@ -229,7 +225,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, encapsulation);
}
else if ( ARP_Analyzer::IsARP(pkt, hdr_size) )
@ -351,7 +347,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, Encapsulation& encapsulation)
{
uint32 caplen = hdr->caplen - hdr_size;
const struct ip* ip4 = ip_hdr->IP4_Hdr();
@ -458,24 +454,10 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
}
#endif
TunnelInfo *tunnel_info = 0;
if ( tunnel_handler )
{
tunnel_info = tunnel_handler->DecapsulateTunnel(ip_hdr, len, caplen, hdr, pkt);
if (tunnel_info)
{
ip4 = tunnel_info->child->IP4_Hdr();
ip_hdr = tunnel_info->child;
len -= tunnel_info->hdr_len;
caplen -= tunnel_info->hdr_len;
}
}
int proto = ip_hdr->NextProto();
if ( CheckHeaderTrunc(proto, len, caplen, hdr, pkt) )
{
delete tunnel_info;
Remove(f);
return;
}
@ -540,9 +522,51 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
break;
}
case IPPROTO_IP:
case IPPROTO_IPV4:
case IPPROTO_IPV6:
{
if ( ! BifConst::Tunnel::decapsulate_ip )
{
reporter->Weird(ip_hdr->SrcAddr(), ip_hdr->DstAddr(), "ip_tunnel");
Remove(f);
return;
}
if ( encapsulation.Depth() >= BifConst::Tunnel::max_depth )
{
reporter->Weird(ip_hdr->SrcAddr(), ip_hdr->DstAddr(), "tunnel_depth");
Remove(f);
return;
}
IP_Hdr* inner_ip;
if ( proto == IPPROTO_IPV6 )
inner_ip = new IP_Hdr((const struct ip6_hdr*) data, false, caplen);
else
inner_ip = new IP_Hdr((const struct ip*) data, false);
struct pcap_pkthdr fake_hdr;
fake_hdr.caplen = fake_hdr.len = caplen;
fake_hdr.ts = hdr->ts;
EncapsulatingConn ec(ip_hdr->SrcAddr(), ip_hdr->DstAddr(),
ip_hdr->IP4_Hdr() ?
( proto == IPPROTO_IPV6 ?
BifEnum::Tunnel::IP6_IN_IP4 : BifEnum::Tunnel::IP4_IN_IP4 ) :
( proto == IPPROTO_IPV6 ?
BifEnum::Tunnel::IP6_IN_IP6 : BifEnum::Tunnel::IP4_IN_IP6 ));
encapsulation.Add(ec);
DoNextPacket(t, &fake_hdr, inner_ip, data, 0, encapsulation);
delete inner_ip;
Remove(f);
return;
}
default:
Weird(fmt("unknown_protocol_%d", proto), hdr, pkt);
delete tunnel_info;
Remove(f);
return;
}
@ -558,7 +582,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, tunnel_info);
conn = NewConn(h, t, &id, data, proto, encapsulation);
if ( conn )
d->Insert(h, conn);
}
@ -569,7 +593,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
if ( consistent < 0 )
{
delete h;
delete tunnel_info;
Remove(f);
return;
}
@ -580,18 +603,20 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
conn->Event(connection_reused, 0);
Remove(conn);
conn = NewConn(h, t, &id, data, proto, tunnel_info);
conn = NewConn(h, t, &id, data, proto, encapsulation);
if ( conn )
d->Insert(h, conn);
}
else
{
delete h;
conn->CheckEncapsulation(encapsulation);
}
}
if ( ! conn )
{
delete h;
delete tunnel_info;
Remove(f);
return;
}
@ -618,8 +643,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
record_packet, record_content,
hdr, pkt, hdr_size);
delete tunnel_info;
if ( f )
{
// Above we already recorded the fragment in its entirety.
@ -651,11 +674,19 @@ bool NetSessions::CheckHeaderTrunc(int proto, uint32 len, uint32 caplen,
case IPPROTO_UDP:
min_hdr_len = sizeof(struct udphdr);
break;
case IPPROTO_IP:
case IPPROTO_IPV4:
min_hdr_len = sizeof(struct ip);
break;
case IPPROTO_IPV6:
min_hdr_len = sizeof(struct ip6_hdr);
break;
case IPPROTO_ICMP:
case IPPROTO_ICMPV6:
default:
// Use for all other packets.
min_hdr_len = ICMP_MINLEN;
break;
}
if ( len < min_hdr_len )
@ -962,14 +993,14 @@ void NetSessions::GetStats(SessionStats& s) const
}
Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id,
const u_char* data, int proto, TunnelInfo* tunnel_info)
const u_char* data, int proto,
const Encapsulation& 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.
int src_h = ntohs(id->src_port);
int dst_h = ntohs(id->dst_port);
int flags = 0;
TunnelParent *tunnel_parent = 0;
// Hmm... This is not great.
TransportProto tproto = TRANSPORT_UNKNOWN;
@ -1019,10 +1050,7 @@ Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id,
id = &flip_id;
}
if ( tunnel_info )
tunnel_parent = new TunnelParent(&(tunnel_info->parent));
Connection* conn = new Connection(this, k, t, id, tunnel_parent);
Connection* conn = new Connection(this, k, t, id, encapsulation);
conn->SetTransport(tproto);
dpm->BuildInitialAnalyzerTree(tproto, conn, data);

View file

@ -14,6 +14,7 @@
struct pcap_pkthdr;
class Encapsulation;
class Connection;
class ConnID;
class OSFingerprint;
@ -26,9 +27,6 @@ class Discarder;
class SteppingStoneManager;
class PacketFilter;
class TunnelHandler;
class TunnelInfo;
class PacketSortElement;
struct SessionStats {
@ -145,7 +143,7 @@ protected:
friend class TimerMgrExpireTimer;
Connection* NewConn(HashKey* k, double t, const ConnID* id,
const u_char* data, int proto, TunnelInfo *tunnel_info);
const u_char* data, int proto, const Encapsulation& encapsulation);
// Check whether the tag of the current packet is consistent with
// the given connection. Returns:
@ -178,7 +176,7 @@ protected:
void DoNextPacket(double t, const struct pcap_pkthdr* hdr,
const IP_Hdr* ip_hdr, const u_char* const pkt,
int hdr_size);
int hdr_size, Encapsulation& encapsulation);
void NextPacketSecondary(double t, const struct pcap_pkthdr* hdr,
const u_char* const pkt, int hdr_size,
@ -216,8 +214,6 @@ 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;

View file

@ -1,178 +1,52 @@
// $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)
RecordVal* EncapsulatingConn::GetRecordVal() const
{
s = arg_s;
PortVal *pv = 0;
TableVal *udp_tunnel_ports = BifConst::Tunnel::udp_tunnel_ports->AsTableVal();
// Find UDP ports we want to analyze. Store them in an array for faster
// lookup.
for ( int i = 0; i< 65536; i++ )
{
if ( pv )
Unref(pv);
pv = new PortVal(i, TRANSPORT_UDP);
if ( udp_tunnel_ports->Lookup(pv, false) )
udp_ports[i] = 1;
else
udp_ports[i] = 0;
}
Unref(pv);
}
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() ) {
case IPPROTO_IPV6: /* 6in4 and 6to4 */
if ( BifConst::Tunnel::decapsulate_ip )
{
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(), false, caplen);
tunnel_info->parent.tunneltype = BifEnum::Tunnel::IP6_IN_IP;
tunnel_info->hdr_len = tunnel_info->child->HdrLen();
tunnel_info->SetParentIPs(ip_hdr);
return tunnel_info;
}
break;
// TODO: IP in IP. Find test traces first. IP proto 0 and/or 4
case IPPROTO_UDP:
if ( BifConst::Tunnel::decapsulate_udp )
{
if ( len < (int)sizeof(struct udphdr) ||
caplen < (int)sizeof(struct udphdr) )
// No weird here. Main packet processing will raise it.
return 0;
return HandleUDP(ip_hdr, len, caplen);
}
RecordVal *rv =
new RecordVal(BifType::Record::Tunnel::EncapsulatingConn);
TransportProto tproto;
switch ( type ) {
case BifEnum::Tunnel::IP6_IN_IP4:
case BifEnum::Tunnel::IP4_IN_IP4:
case BifEnum::Tunnel::IP6_IN_IP6:
case BifEnum::Tunnel::IP4_IN_IP6:
tproto = TRANSPORT_UNKNOWN;
break;
default:
tproto = TRANSPORT_UDP;
break;
} /* end switch */
return 0;
} // end switch
RecordVal* id_val = new RecordVal(conn_id);
id_val->Assign(0, new AddrVal(src_addr));
id_val->Assign(1, new PortVal(ntohs(src_port), tproto));
id_val->Assign(2, new AddrVal(dst_addr));
id_val->Assign(3, new PortVal(ntohs(dst_port), tproto));
rv->Assign(0, id_val);
rv->Assign(1, new EnumVal(type, BifType::Enum::Tunnel::Type));
return rv;
}
TunnelInfo* TunnelHandler::HandleUDP(const IP_Hdr *ip_hdr, int len, int caplen)
bool operator==(const Encapsulation& e1, const Encapsulation& e2)
{
// We already know we that we have a valid UDP header
const u_char *data = ip_hdr->Payload();
const struct udphdr* uh = (const struct udphdr*)data;
IP_Hdr *cand_ip_hdr = 0;
BifEnum::Tunnel::Tunneltype tunneltype = BifEnum::Tunnel::NONE;
int hdr_len = sizeof(struct udphdr);
data += hdr_len;
int datalen = (int)ntohs(uh->uh_ulen);
datalen = min(datalen, min(len, caplen));
datalen -= hdr_len;
if ( BifConst::Tunnel::udp_tunnel_allports ||
udp_ports[ntohs(uh->uh_sport)] ||
udp_ports[ntohs(uh->uh_dport)] )
{
cand_ip_hdr = LookForIPHdr(data, datalen);
if ( cand_ip_hdr )
if ( e1.conns )
if ( e2.conns )
{
// Found and IP hdr directly in the UDP payload
tunneltype = (cand_ip_hdr->IP4_Hdr()) ?
BifEnum::Tunnel::IP4_IN_UDP : BifEnum::Tunnel::IP6_IN_UDP;
if ( e1.conns->size() != e2.conns->size() )
return false;
else
for ( size_t i = 0; i < e1.conns->size(); ++i )
if ( (*e1.conns)[i] != (*e2.conns)[i] )
return false;
return true;
}
else if ( datalen >= 8 )
{
// Look for AYIAY tunnels
u_char id_byte = data[0];
u_char sig_byte = data[1];
u_char next_hdr = data[3];
// identity length field is high bits of id_byte.
// length in octets is 2 to the power of length field
int id_len = (1 << (id_byte>>4));
// signature length field is high bits of sig_byte
// length in octets 4 * length field
int sig_len = 4*(sig_byte>>4);
datalen -= 8 + id_len + sig_len;
data += 8 + id_len + sig_len;
if ( datalen <= 0 )
return 0;
cand_ip_hdr = LookForIPHdr(data, datalen);
if ( cand_ip_hdr )
{
hdr_len += 8 + id_len + sig_len;
tunneltype = (cand_ip_hdr->IP4_Hdr()) ?
BifEnum::Tunnel::IP4_IN_AYIAY : BifEnum::Tunnel::IP6_IN_AYIAY;
}
}
if ( cand_ip_hdr )
{
TunnelInfo *tunnel_info = new TunnelInfo();
tunnel_info->child = cand_ip_hdr;
tunnel_info->parent.tunneltype = tunneltype;
tunnel_info->SetParentIPs(ip_hdr);
tunnel_info->SetParentPorts(uh);
tunnel_info->hdr_len = hdr_len + cand_ip_hdr->HdrLen();
return tunnel_info;
};
}
return 0;
}
IP_Hdr* TunnelHandler::LookForIPHdr(const u_char *data, int datalen)
{
IP_Hdr *cand_ip_hdr = 0;
if (datalen < (int)sizeof(struct ip))
return 0;
const struct ip *ip4 = (const struct ip*)(data);
if ( ip4->ip_v == 4 )
cand_ip_hdr = new IP_Hdr((const struct ip*)ip4, false);
else if ( ip4->ip_v == 6 && (datalen > (int)sizeof(struct ip6_hdr)) )
cand_ip_hdr = new IP_Hdr((const struct ip6_hdr*)data, false, datalen);
if ( cand_ip_hdr )
{
switch ( cand_ip_hdr->NextProto() ) {
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_ICMP:
if ( (int)cand_ip_hdr->TotalLen() != datalen )
{
delete cand_ip_hdr;
cand_ip_hdr = 0;
}
break;
default:
delete cand_ip_hdr;
cand_ip_hdr = 0;
break;
} // end switch
}
return cand_ip_hdr;
else
return false;
else
if ( e2.conns )
return false;
else
return true;
}

View file

@ -3,105 +3,109 @@
#ifndef tunnelhandler_h
#define tunnelhandler_h
#include <netinet/udp.h>
#include "net_util.h"
#include "IP.h"
#include "config.h"
#include "NetVar.h"
#include "IPAddr.h"
#include "Conn.h"
#include "Sessions.h"
#include "Val.h"
#include <vector>
class TunnelParent {
class EncapsulatingConn {
public:
TunnelParent()
EncapsulatingConn()
: src_port(0), dst_port(0), type(BifEnum::Tunnel::NONE) {}
EncapsulatingConn(const IPAddr& s, const IPAddr& d,
BifEnum::Tunnel::Type t)
: src_addr(s), dst_addr(d), src_port(0), dst_port(0), type(t) {}
EncapsulatingConn(const IPAddr& s, const IPAddr& d, uint16 sp, uint16 dp,
BifEnum::Tunnel::Type t)
: src_addr(s), dst_addr(d), src_port(sp), dst_port(dp), type(t) {}
EncapsulatingConn(const EncapsulatingConn& other)
: src_addr(other.src_addr), dst_addr(other.dst_addr),
src_port(other.src_port), dst_port(other.dst_port),
type(other.type) {}
~EncapsulatingConn() {}
RecordVal* GetRecordVal() const;
friend bool operator==(const EncapsulatingConn& ec1,
const EncapsulatingConn& ec2)
{
tunneltype = BifEnum::Tunnel::NONE;
src_port = dst_port = 0;
return ec1.type == ec2.type && ec1.src_addr == ec2.src_addr &&
ec1.src_port == ec2.src_port && ec1.dst_port == ec2.dst_port;
}
TunnelParent(TunnelParent *other)
friend bool operator!=(const EncapsulatingConn& ec1,
const EncapsulatingConn& ec2)
{
tunneltype = other->tunneltype;
src_addr = other->src_addr;
dst_addr = other->dst_addr;
src_port = other->src_port;
dst_port = other->dst_port;
}
RecordVal* GetRecordVal() const
{
RecordVal *rv = new RecordVal(BifType::Record::Tunnel::Parent);
TransportProto tproto;
switch ( tunneltype ) {
case BifEnum::Tunnel::IP6_IN_IP:
case BifEnum::Tunnel::IP4_IN_IP:
tproto = TRANSPORT_UNKNOWN;
break;
default:
tproto = TRANSPORT_UDP;
} // end switch
RecordVal* id_val = new RecordVal(conn_id);
id_val->Assign(0, new AddrVal(src_addr));
id_val->Assign(1, new PortVal(ntohs(src_port), tproto));
id_val->Assign(2, new AddrVal(dst_addr));
id_val->Assign(3, new PortVal(ntohs(dst_port), tproto));
rv->Assign(0, id_val);
rv->Assign(1, new EnumVal(tunneltype, BifType::Enum::Tunnel::Tunneltype));
return rv;
return ! ( ec1 == ec2 );
}
IPAddr src_addr;
IPAddr dst_addr;
uint16 src_port;
uint16 dst_port;
BifEnum::Tunnel::Tunneltype tunneltype;
BifEnum::Tunnel::Type type;
};
class TunnelInfo {
class Encapsulation {
public:
TunnelInfo()
Encapsulation() : conns(0) {}
Encapsulation(const Encapsulation& other)
{
child = 0;
hdr_len = 0;
}
~TunnelInfo()
{
if (child) delete child;
if ( other.conns )
conns = new vector<EncapsulatingConn>(*(other.conns));
else
conns = 0;
}
void SetParentIPs(const IP_Hdr *ip_hdr)
Encapsulation& operator=(const Encapsulation& other)
{
parent.src_addr = ip_hdr->SrcAddr();
parent.dst_addr = ip_hdr->DstAddr();
}
void SetParentPorts(const struct udphdr *uh)
{
parent.src_port = uh->uh_sport;
parent.dst_port = uh->uh_dport;
if ( this == &other ) return *this;
delete conns;
if ( other.conns )
conns = new vector<EncapsulatingConn>(*(other.conns));
else
conns = 0;
return *this;
}
IP_Hdr *child;
TunnelParent parent;
int hdr_len;
~Encapsulation() { delete conns; }
void Add(const EncapsulatingConn& c)
{
if ( ! conns )
conns = new vector<EncapsulatingConn>();
conns->push_back(c);
}
size_t Depth() const
{
return conns ? conns->size() : 0;
}
VectorVal* GetVectorVal() const
{
VectorVal* vv = new VectorVal(new VectorType(
BifType::Record::Tunnel::EncapsulatingConn->Ref()));
if ( conns )
for ( size_t i = 0; i < conns->size(); ++i )
vv->Assign(i, (*conns)[i].GetRecordVal(), 0);
return vv;
}
friend bool operator==(const Encapsulation& e1, const Encapsulation& e2);
friend bool operator!=(const Encapsulation& e1, const Encapsulation& e2)
{
return ! ( e1 == e2 );
}
vector<EncapsulatingConn>* conns;
};
class TunnelHandler {
public:
TunnelHandler(NetSessions *arg_s);
~TunnelHandler();
// Main entry point. Returns a nil if not tunneled.
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;
short udp_ports[65536]; // which UDP ports to decapsulate
IP_Hdr* LookForIPHdr(const u_char *data, int datalen);
TunnelInfo* HandleUDP(const IP_Hdr *ip_hdr, int len, int caplen);
};
#endif

View file

@ -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;
@ -16,3 +15,4 @@ const Tunnel::decapsulate_ip: bool;
const Tunnel::decapsulate_udp: bool;
const Tunnel::udp_tunnel_ports: any;
const Tunnel::udp_tunnel_allports: bool;
const Tunnel::max_depth: count;

View file

@ -141,6 +141,17 @@ event dns_mapping_altered%(dm: dns_mapping, old_addrs: addr_set, new_addrs: addr
## event.
event new_connection%(c: connection%);
## 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 the connection's
## *tunnel* field is NOT automatically assigned to the new encapsulation value
## internally after this event is raised.
##
## c: The connection whose tunnel/encapsulation changed.
##
## e: The new encapsulation.
event tunnel_changed%(c: connection, e: encapsulating_conns%);
## 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.

View file

@ -170,16 +170,18 @@ enum ID %{
module Tunnel;
enum Tunneltype %{
enum Type %{
NONE,
IP6_IN_IP,
IP4_IN_IP,
IP6_IN_IP4,
IP4_IN_IP4,
IP6_IN_IP6,
IP4_IN_IP6,
IP6_IN_UDP,
IP4_IN_UDP,
IP6_IN_AYIAY,
IP4_IN_AYIAY,
%}
type Parent: record;
type EncapsulatingConn: record;
module GLOBAL;

View file

@ -0,0 +1,22 @@
new_connection: tunnel
conn_id: [orig_h=dead::beef, orig_p=30000/udp, resp_h=cafe::babe, resp_p=13000/udp]
encap: [[cid=[orig_h=2001:4f8:4:7:2e0:81ff:fe52:ffff, orig_p=0/unknown, resp_h=2001:4f8:4:7:2e0:81ff:fe52:9a6b, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP6]]
new_connection: tunnel
conn_id: [orig_h=dead::beef, orig_p=30000/udp, resp_h=cafe::babe, resp_p=13000/udp]
encap: [[cid=[orig_h=feed::beef, orig_p=0/unknown, resp_h=feed::cafe, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP6], [cid=[orig_h=babe::beef, orig_p=0/unknown, resp_h=dead::babe, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP6]]
new_connection: tunnel
conn_id: [orig_h=dead::beef, orig_p=30000/udp, resp_h=cafe::babe, resp_p=13000/udp]
encap: [[cid=[orig_h=1.2.3.4, orig_p=0/unknown, resp_h=5.6.7.8, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP4]]
new_connection: tunnel
conn_id: [orig_h=70.55.213.211, orig_p=31337/tcp, resp_h=192.88.99.1, resp_p=80/tcp]
encap: [[cid=[orig_h=2002:4637:d5d3::4637:d5d3, orig_p=0/unknown, resp_h=2001:4860:0:2001::68, resp_p=0/unknown], tunnel_type=Tunnel::IP4_IN_IP6]]
new_connection: tunnel
conn_id: [orig_h=10.0.0.1, orig_p=30000/udp, resp_h=10.0.0.2, resp_p=13000/udp]
encap: [[cid=[orig_h=1.2.3.4, orig_p=0/unknown, resp_h=5.6.7.8, resp_p=0/unknown], tunnel_type=Tunnel::IP4_IN_IP4]]
new_connection: tunnel
conn_id: [orig_h=dead::beef, orig_p=30000/udp, resp_h=cafe::babe, resp_p=13000/udp]
encap: [[cid=[orig_h=2001:4f8:4:7:2e0:81ff:fe52:ffff, orig_p=0/unknown, resp_h=2001:4f8:4:7:2e0:81ff:fe52:9a6b, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP6]]
tunnel_changed:
conn_id: [orig_h=dead::beef, orig_p=30000/udp, resp_h=cafe::babe, resp_p=13000/udp]
old: [[cid=[orig_h=2001:4f8:4:7:2e0:81ff:fe52:ffff, orig_p=0/unknown, resp_h=2001:4f8:4:7:2e0:81ff:fe52:9a6b, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP6]]
new: [[cid=[orig_h=feed::beef, orig_p=0/unknown, resp_h=feed::cafe, resp_p=0/unknown], tunnel_type=Tunnel::IP6_IN_IP6]]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,30 @@
# @TEST-EXEC: bro -b -r $TRACES/tunnels/6in6.pcap %INPUT >>output 2>&1
# @TEST-EXEC: bro -b -r $TRACES/tunnels/6in6in6.pcap %INPUT >>output 2>&1
# @TEST-EXEC: bro -b -r $TRACES/tunnels/6in4.pcap %INPUT >>output 2>&1
# @TEST-EXEC: bro -b -r $TRACES/tunnels/4in6.pcap %INPUT >>output 2>&1
# @TEST-EXEC: bro -b -r $TRACES/tunnels/4in4.pcap %INPUT >>output 2>&1
# @TEST-EXEC: bro -b -r $TRACES/tunnels/6in6-tunnel-change.pcap %INPUT >>output 2>&1
# @TEST-EXEC: btest-diff output
event new_connection(c: connection)
{
if ( c?$tunnel )
{
print "new_connection: tunnel";
print fmt(" conn_id: %s", c$id);
print fmt(" encap: %s", c$tunnel);
}
else
{
print "new_connection: no tunnel";
}
}
event tunnel_changed(c: connection, e: encapsulating_conns)
{
print "tunnel_changed:";
print fmt(" conn_id: %s", c$id);
if ( c?$tunnel )
print fmt(" old: %s", c$tunnel);
print fmt(" new: %s", e);
}