diff --git a/policy/bro.init b/policy/bro.init index 17607a7113..8be0ab754a 100644 --- a/policy/bro.init +++ b/policy/bro.init @@ -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 diff --git a/src/Sessions.cc b/src/Sessions.cc index 48fab1bff4..41a7f4c52d 100644 --- a/src/Sessions.cc +++ b/src/Sessions.cc @@ -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); diff --git a/src/TunnelHandler.cc b/src/TunnelHandler.cc index c739403ad8..3256894cd0 100644 --- a/src/TunnelHandler.cc +++ b/src/TunnelHandler.cc @@ -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; + } diff --git a/src/TunnelHandler.h b/src/TunnelHandler.h index aa4cae0a39..31c9791a1c 100644 --- a/src/TunnelHandler.h +++ b/src/TunnelHandler.h @@ -3,6 +3,7 @@ #ifndef tunnelhandler_h #define tunnelhandler_h +#include #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); }; diff --git a/src/const.bif b/src/const.bif index 825c21e7a5..447812a902 100644 --- a/src/const.bif +++ b/src/const.bif @@ -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; diff --git a/src/types.bif b/src/types.bif index d44f177b82..35c4db0daf 100644 --- a/src/types.bif +++ b/src/types.bif @@ -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;