Merge branch 'master' into fastpath

This commit is contained in:
Jon Siwek 2012-12-11 16:21:23 -06:00
commit 4c278ce387
57 changed files with 716 additions and 4 deletions

View file

@ -20,5 +20,13 @@ void AYIYA_Analyzer::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);
try
{
interp->NewData(orig, data, data + len);
}
catch ( const binpac::Exception& e )
{
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
}
}

View file

@ -41,6 +41,7 @@
#include "Syslog-binpac.h"
#include "Teredo.h"
#include "ConnSizeAnalyzer.h"
#include "GTPv1.h"
// Keep same order here as in AnalyzerTag definition!
const Analyzer::Config Analyzer::analyzer_configs[] = {
@ -143,6 +144,9 @@ const Analyzer::Config Analyzer::analyzer_configs[] = {
{ AnalyzerTag::Teredo, "TEREDO",
Teredo_Analyzer::InstantiateAnalyzer,
Teredo_Analyzer::Available, 0, false },
{ AnalyzerTag::GTPv1, "GTPV1",
GTPv1_Analyzer::InstantiateAnalyzer,
GTPv1_Analyzer::Available, 0, false },
{ AnalyzerTag::File, "FILE", File_Analyzer::InstantiateAnalyzer,
File_Analyzer::Available, 0, false },

View file

@ -38,6 +38,7 @@ namespace AnalyzerTag {
AYIYA,
SOCKS,
Teredo,
GTPv1,
// Other
File, Backdoor, InterConn, SteppingStone, TCPStats,

View file

@ -203,6 +203,8 @@ binpac_target(dns.pac
dns-protocol.pac dns-analyzer.pac)
binpac_target(dns_tcp.pac
dns.pac)
binpac_target(gtpv1.pac
gtpv1-protocol.pac gtpv1-analyzer.pac)
binpac_target(http.pac
http-protocol.pac http-analyzer.pac)
binpac_target(ncp.pac)
@ -332,6 +334,7 @@ set(bro_SRCS
Frame.cc
Func.cc
Gnutella.cc
GTPv1.cc
HTTP.cc
HTTP-binpac.cc
Hash.cc

31
src/GTPv1.cc Normal file
View file

@ -0,0 +1,31 @@
#include "GTPv1.h"
GTPv1_Analyzer::GTPv1_Analyzer(Connection* conn)
: Analyzer(AnalyzerTag::GTPv1, conn)
{
interp = new binpac::GTPv1::GTPv1_Conn(this);
}
GTPv1_Analyzer::~GTPv1_Analyzer()
{
delete interp;
}
void GTPv1_Analyzer::Done()
{
Analyzer::Done();
Event(udp_session_done);
}
void GTPv1_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);
try
{
interp->NewData(orig, data, data + len);
}
catch ( const binpac::Exception& e )
{
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
}
}

29
src/GTPv1.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef GTPv1_h
#define GTPv1_h
#include "gtpv1_pac.h"
class GTPv1_Analyzer : public Analyzer {
public:
GTPv1_Analyzer(Connection* conn);
virtual ~GTPv1_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 GTPv1_Analyzer(conn); }
static bool Available()
{ return BifConst::Tunnel::enable_gtpv1 &&
BifConst::Tunnel::max_depth > 0; }
protected:
friend class AnalyzerTimer;
void ExpireTimer(double t);
binpac::GTPv1::GTPv1_Conn* interp;
};
#endif

View file

@ -5,6 +5,7 @@
#include "Var.h"
#include "NetVar.h"
RecordType* gtpv1_hdr_type;
RecordType* conn_id;
RecordType* endpoint;
RecordType* endpoint_stats;
@ -308,6 +309,7 @@ void init_net_var()
#include "input.bif.netvar_init"
#include "reporter.bif.netvar_init"
gtpv1_hdr_type = internal_type("gtpv1_hdr")->AsRecordType();
conn_id = internal_type("conn_id")->AsRecordType();
endpoint = internal_type("endpoint")->AsRecordType();
endpoint_stats = internal_type("endpoint_stats")->AsRecordType();

View file

@ -8,6 +8,7 @@
#include "EventRegistry.h"
#include "Stats.h"
extern RecordType* gtpv1_hdr_type;
extern RecordType* conn_id;
extern RecordType* endpoint;
extern RecordType* endpoint_stats;

View file

@ -15,8 +15,10 @@ const Tunnel::max_depth: count;
const Tunnel::enable_ip: bool;
const Tunnel::enable_ayiya: bool;
const Tunnel::enable_teredo: bool;
const Tunnel::enable_gtpv1: bool;
const Tunnel::yielding_teredo_decapsulation: bool;
const Tunnel::delay_teredo_confirmation: bool;
const Tunnel::delay_gtp_confirmation: bool;
const Tunnel::ip_tunnel_timeout: interval;
const Threading::heartbeat_interval: interval;

View file

@ -577,6 +577,19 @@ event teredo_origin_indication%(outer: connection, inner: teredo_hdr%);
## it may become particularly expensive for real-time analysis.
event teredo_bubble%(outer: connection, inner: teredo_hdr%);
## Generated for GTPv1 G-PDU packets. That is, packets with a UDP payload
## that includes a GTP header followed by an IPv4 or IPv6 packet.
##
## outer: The GTP outer tunnel connection.
##
## inner_gtp: The GTP header.
##
## inner_ip: The inner IP and transport layer packet headers.
##
## .. note:: Since this event may be raised on a per-packet basis, handling
## it may become particularly expensive for real-time analysis.
event gtpv1_g_pdu_packet%(outer: connection, inner_gtp: gtpv1_hdr, inner_ip: pkt_hdr%);
## 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
@ -858,7 +871,7 @@ 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
## this event. Currently, the analyzers for DNS, NTP, Netbios, Syslog, AYIYA,
## and Teredo support this.
## Teredo, and GTPv1 support this.
##
## u: The connection record for the corresponding UDP flow.
##

161
src/gtpv1-analyzer.pac Normal file
View file

@ -0,0 +1,161 @@
connection GTPv1_Conn(bro_analyzer: BroAnalyzer)
{
upflow = GTPv1_Flow(true);
downflow = GTPv1_Flow(false);
%member{
bool valid_orig;
bool valid_resp;
%}
%init{
valid_orig = valid_resp = false;
%}
function valid(orig: bool): bool
%{
return orig ? valid_orig : valid_resp;
%}
function set_valid(orig: bool, val: bool): void
%{
if ( orig )
valid_orig = val;
else
valid_resp = val;
%}
}
%code{
inline void violate(const char* r, const BroAnalyzer& a, const bytestring& p)
{
a->ProtocolViolation(r, (const char*) p.data(), p.length());
}
%}
flow GTPv1_Flow(is_orig: bool)
{
datagram = GTPv1_Header withcontext(connection, this);
function process_gtpv1(pdu: GTPv1_Header): bool
%{
BroAnalyzer a = connection()->bro_analyzer();
Connection *c = a->Conn();
const EncapsulationStack* e = c->GetEncapsulation();
connection()->set_valid(is_orig(), false);
if ( e && e->Depth() >= BifConst::Tunnel::max_depth )
{
reporter->Weird(c, "tunnel_depth");
return false;
}
if ( e && e->LastType() == BifEnum::Tunnel::GTPv1 )
{
// GTP is never tunneled in GTP so, this must be a regular packet
violate("GTP-in-GTP", a, ${pdu.packet});
return false;
}
if ( ${pdu.version} != 1 )
{
// Only know of GTPv1 with Version == 1
violate("GTPv1 bad Version", a, ${pdu.packet});
return false;
}
if ( ! ${pdu.pt_flag} )
{
// Not interested in GTP'
return false;
}
if ( ${pdu.e_flag} )
{
// TODO: can't currently parse past extension headers
return false;
}
if ( ${pdu.msg_type} != 0xff )
{
// Only interested in decapsulating user plane data beyond here.
return false;
}
if ( ${pdu.packet}.length() < (int)sizeof(struct ip) )
{
violate("Truncated GTPv1", a, ${pdu.packet});
return false;
}
const struct ip* ip = (const struct ip*) ${pdu.packet}.data();
if ( ip->ip_v != 4 && ip->ip_v != 6 )
{
violate("non-IP packet in GTPv1", a, ${pdu.packet});
return false;
}
IP_Hdr* inner = 0;
int result = sessions->ParseIPPacket(${pdu.packet}.length(),
${pdu.packet}.data(), ip->ip_v == 6 ? IPPROTO_IPV6 : IPPROTO_IPV4,
inner);
if ( result == 0 )
{
connection()->set_valid(is_orig(), true);
if ( (! BifConst::Tunnel::delay_gtp_confirmation) ||
(connection()->valid(true) && connection()->valid(false)) )
a->ProtocolConfirmation();
}
else if ( result < 0 )
violate("Truncated GTPv1", a, ${pdu.packet});
else
violate("GTPv1 payload length", a, ${pdu.packet});
if ( result != 0 )
{
delete inner;
return false;
}
if ( ::gtpv1_g_pdu_packet )
{
RecordVal* rv = new RecordVal(gtpv1_hdr_type);
rv->Assign(0, new Val(${pdu.version}, TYPE_COUNT));
rv->Assign(1, new Val(${pdu.pt_flag}, TYPE_BOOL));
rv->Assign(2, new Val(${pdu.rsv}, TYPE_BOOL));
rv->Assign(3, new Val(${pdu.e_flag}, TYPE_BOOL));
rv->Assign(4, new Val(${pdu.s_flag}, TYPE_BOOL));
rv->Assign(5, new Val(${pdu.pn_flag}, TYPE_BOOL));
rv->Assign(6, new Val(${pdu.msg_type}, TYPE_COUNT));
rv->Assign(7, new Val(ntohs(${pdu.length}), TYPE_COUNT));
rv->Assign(8, new Val(ntohl(${pdu.teid}), TYPE_COUNT));
if ( ${pdu.has_opt} )
{
rv->Assign(9, new Val(ntohs(${pdu.opt_hdr.seq}), TYPE_COUNT));
rv->Assign(10, new Val(${pdu.opt_hdr.n_pdu}, TYPE_COUNT));
rv->Assign(11, new Val(${pdu.opt_hdr.next_type}, TYPE_COUNT));
}
BifEvent::generate_gtpv1_g_pdu_packet(a, c, rv,
inner->BuildPktHdrVal());
}
EncapsulatingConn ec(c, BifEnum::Tunnel::GTPv1);
sessions->DoNextInnerPacket(network_time(), 0, inner, e, ec);
return (result == 0) ? true : false;
%}
};
refine typeattr GTPv1_Header += &let { proc_gtpv1 = $context.flow.process_gtpv1(this); };

27
src/gtpv1-protocol.pac Normal file
View file

@ -0,0 +1,27 @@
type GTPv1_Header = record {
flags: uint8;
msg_type: uint8;
length: uint16;
teid: uint32;
opt: case has_opt of {
true -> opt_hdr: GTPv1_Opt_Header;
false -> no_opt: empty;
} &requires(has_opt);
packet: bytestring &restofdata;
} &let {
version: uint8 = (flags & 0xE0) >> 5;
pt_flag: bool = flags & 0x10;
rsv: bool = flags & 0x08;
e_flag: bool = flags & 0x04;
s_flag: bool = flags & 0x02;
pn_flag: bool = flags & 0x01;
has_opt: bool = flags & 0x07;
} &byteorder = littleendian;
type GTPv1_Opt_Header = record {
seq: uint16;
n_pdu: uint8;
next_type: uint8;
}

10
src/gtpv1.pac Normal file
View file

@ -0,0 +1,10 @@
%include binpac.pac
%include bro.pac
analyzer GTPv1 withcontext {
connection: GTPv1_Conn;
flow: GTPv1_Flow;
};
%include gtpv1-protocol.pac
%include gtpv1-analyzer.pac

View file

@ -184,6 +184,7 @@ enum Type %{
AYIYA,
TEREDO,
SOCKS,
GTPv1,
%}
type EncapsulatingConn: record;