Adding support to de-capsulate tunnels. Checkpoint.

Re-organizing code. Adding UDP tunnel handlers.
Using policy level redef'able consts to tune behavior. UDP port settings
not working yet.
This commit is contained in:
Gregor Maier 2011-08-04 15:25:13 -07:00
parent 9c388a1809
commit 8910cd2dca
6 changed files with 216 additions and 40 deletions

View file

@ -81,10 +81,14 @@ type endpoint_stats: record {
type AnalyzerID: count;
type tunnel_parent_t: record {
cid: conn_id;
tunnel_type: tunneltype_t;
};
module Tunnel;
export {
type parent_t: record {
cid: conn_id;
tunnel_type: tunneltype_t;
};
} # end export
module GLOBAL;
type connection: record {
id: conn_id;
@ -97,7 +101,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;
tunnel_parent: Tunnel::parent_t &optional;
};
type SYN_packet: record {
@ -1489,6 +1493,28 @@ const skip_http_data = F &redef;
# UDP tunnels. See also: udp_tunnel_port, policy/udp-tunnel.bro.
const parse_udp_tunnels = F &redef;
module Tunnel;
export {
# Whether to decapsulate IP tunnels (IPinIP, 6in4, 6to4)
const decapsulate_ip = F &redef;
# Whether to decapsulate URDP tunnels (e.g., Teredo, IPv4 in UDP)
const decapsulate_udp = F &redef;
# If decapsulating UDP: the set of ports for which to do so
const udp_tunnel_ports: set[port] = {
3544/udp, # Teredo
5072/udp, # AYIAY
} &redef;
# If udp_tunnel_allports is T udp_tunnel_ports is ignored and we
# check every UDP packet for tunnels.
const udp_tunnel_allports = F &redef;
} # end export
module GLOBAL;
# Load the site utilities.
@load utils/site

View file

@ -131,10 +131,11 @@ NetSessions::NetSessions()
arp_analyzer = 0;
if ( 1 )
if ( BifConst::Tunnel::decapsulate_ip || BifConst::Tunnel::decapsulate_udp )
tunnel_handler = new TunnelHandler(this);
else
tunnel_handler = 0;
printf("tunnel_handler: %p\n", tunnel_handler);
}
NetSessions::~NetSessions()
@ -473,13 +474,20 @@ 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)
len -= ip_hdr_len; // remove IP header
caplen -= ip_hdr_len;
TunnelInfo *tunnel_info = 0;
if ( tunnel_handler )
{
ip4 = tunnel_info->child->IP4_Hdr();
ip_hdr = tunnel_info->child;
len -= tunnel_info->hdr_len;
caplen -= tunnel_info->hdr_len;
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();
@ -490,9 +498,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
return;
}
len -= ip_hdr_len; // remove IP header
caplen -= ip_hdr_len;
uint32 min_hdr_len = (proto == IPPROTO_TCP) ? sizeof(struct tcphdr) :
(proto == IPPROTO_UDP ? sizeof(struct udphdr) : ICMP_MINLEN);

View file

@ -18,31 +18,151 @@
TunnelHandler::TunnelHandler(NetSessions *arg_s)
{
s = arg_s;
for (int i=0; i< 65536; i++)
udp_ports[i] = 0;
udp_ports[3544] = 1;
udp_ports[5072] = 1;
}
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;
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());
tunnel_info->tunneltype = BifEnum::Tunnel::IP6inIP;
tunnel_info->hdr_len = tunnel_info->child->HdrLen();
tunnel_info->SetParentIPs(ip_hdr);
return tunnel_info;
}
break;
#endif
// 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);
}
break;
default:
break;
} /* end switch */
return 0;
}
TunnelInfo* TunnelHandler::HandleUDP(const IP_Hdr *ip_hdr, int len, int caplen)
{
// 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_t 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)
{
tunneltype = (cand_ip_hdr->IP4_Hdr()) ?
BifEnum::Tunnel::IP4inUDP : BifEnum::Tunnel::IP6inUDP;
}
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::IP4inAYIAY : BifEnum::Tunnel::IP6inAYIAY;
}
}
if (cand_ip_hdr)
{
TunnelInfo *tunnel_info = new TunnelInfo();
tunnel_info->child = cand_ip_hdr;
tunnel_info->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);
else if (ip4->ip_v == 6 && (datalen > (int)sizeof(struct ip6_hdr)))
cand_ip_hdr = new IP_Hdr((const struct ip6_hdr*)data);
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;
}

View file

@ -3,6 +3,7 @@
#ifndef tunnelhandler_h
#define tunnelhandler_h
#include <netinet/udp.h>
#include "IP.h"
#include "Conn.h"
#include "Sessions.h"
@ -14,7 +15,7 @@ public:
TunnelInfo()
{
child = 0;
tunneltype = BifEnum::NONE;
tunneltype = BifEnum::Tunnel::NONE;
hdr_len = 0;
parent.src_addr = parent.dst_addr = 0;
parent.src_port = parent.dst_port = 0;
@ -30,30 +31,39 @@ public:
parent.src_addr = ip_hdr->SrcAddr();
parent.dst_addr = ip_hdr->DstAddr();
}
void SetParentPorts(uint32 src_port, uint32 dst_port)
void SetParentPorts(const struct udphdr *uh)
{
parent.src_port = src_port;
parent.dst_port = dst_port;
parent.src_port = uh->uh_sport;
parent.dst_port = uh->uh_dport;
}
RecordVal* GetRecordVal() const
{
RecordVal *rv = new RecordVal(BifType::Record::tunnel_parent_t);
RecordVal *rv = new RecordVal(BifType::Record::Tunnel::parent_t);
TransportProto tproto;
switch(tunneltype) {
case BifEnum::Tunnel::IP6inIP:
case BifEnum::Tunnel::IP4inIP:
tproto = TRANSPORT_UNKNOWN;
break;
default:
tproto = TRANSPORT_UDP;
} // end switch
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(1, new PortVal(ntohs(parent.src_port), tproto));
id_val->Assign(2, new AddrVal(parent.dst_addr));
id_val->Assign(3, new PortVal(ntohs(parent.dst_port), TRANSPORT_UNKNOWN));
id_val->Assign(3, new PortVal(ntohs(parent.dst_port), tproto));
rv->Assign(0, id_val);
rv->Assign(1, new EnumVal(tunneltype, BifType::Enum::tunneltype_t));
rv->Assign(1, new EnumVal(tunneltype, BifType::Enum::Tunnel::tunneltype_t));
return rv;
}
IP_Hdr *child;
ConnID parent;
int hdr_len;
BifEnum::tunneltype_t tunneltype;
BifEnum::Tunnel::tunneltype_t tunneltype;
};
class TunnelHandler {
@ -61,12 +71,16 @@ 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() */
// 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);
};

View file

@ -12,3 +12,7 @@ const NFS3::return_data: bool;
const NFS3::return_data_max: count;
const NFS3::return_data_first_only: bool;
const Tunnel::decapsulate_ip: bool;
const Tunnel::decapsulate_udp: bool;
const Tunnel::udp_tunnel_ports: any;
const Tunnel::udp_tunnel_allports: bool;

View file

@ -166,11 +166,18 @@ enum ID %{
Unknown,
%}
module GLOBAL;
module Tunnel;
enum tunneltype_t %{
NONE,
IP6inIP,
IP6inIP,
IP4inIP,
IP6inUDP,
IP4inUDP,
IP6inAYIAY,
IP4inAYIAY,
%}
type tunnel_parent_t: record;
type parent_t: record;
module GLOBAL;