diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 7129d8eb68..88f0910d1c 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -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; diff --git a/scripts/policy/frameworks/tunnel.bro b/scripts/policy/frameworks/tunnel.bro index 98a860653c..fb9bf2f3f6 100644 --- a/scripts/policy/frameworks/tunnel.bro +++ b/scripts/policy/frameworks/tunnel.bro @@ -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 diff --git a/src/Conn.cc b/src/Conn.cc index c2008b1faa..80c026e781 100644 --- a/src/Conn.cc +++ b/src/Conn.cc @@ -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 ) diff --git a/src/Conn.h b/src/Conn.h index 99af34a07a..9cdb746b7c 100644 --- a/src/Conn.h +++ b/src/Conn.h @@ -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; diff --git a/src/NetVar.cc b/src/NetVar.cc index 59cc1cc633..390598bb99 100644 --- a/src/NetVar.cc +++ b/src/NetVar.cc @@ -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"); diff --git a/src/NetVar.h b/src/NetVar.h index 425ea93e09..f5c17f64aa 100644 --- a/src/NetVar.h +++ b/src/NetVar.h @@ -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; diff --git a/src/Sessions.cc b/src/Sessions.cc index 7d829b602b..769bd68f52 100644 --- a/src/Sessions.cc +++ b/src/Sessions.cc @@ -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); diff --git a/src/Sessions.h b/src/Sessions.h index edbb7a8ffd..e1afbeec5a 100644 --- a/src/Sessions.h +++ b/src/Sessions.h @@ -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 TimerMgrMap; diff --git a/src/TunnelHandler.cc b/src/TunnelHandler.cc index 9debf79d9c..4923b36f3d 100644 --- a/src/TunnelHandler.cc +++ b/src/TunnelHandler.cc @@ -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 - -#include -#include - #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; } diff --git a/src/TunnelHandler.h b/src/TunnelHandler.h index 9f3f3614e7..21e491cdc9 100644 --- a/src/TunnelHandler.h +++ b/src/TunnelHandler.h @@ -3,105 +3,109 @@ #ifndef tunnelhandler_h #define tunnelhandler_h -#include -#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 -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(*(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(*(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(); + 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* 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 diff --git a/src/const.bif b/src/const.bif index 0ebd210a95..b622d52ff3 100644 --- a/src/const.bif +++ b/src/const.bif @@ -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; diff --git a/src/event.bif b/src/event.bif index 1ce8907f0b..b92354e632 100644 --- a/src/event.bif +++ b/src/event.bif @@ -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. diff --git a/src/types.bif b/src/types.bif index 0017c4b6ff..60f8631a23 100644 --- a/src/types.bif +++ b/src/types.bif @@ -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; diff --git a/testing/btest/Baseline/core.tunnels.ip-in-ip/output b/testing/btest/Baseline/core.tunnels.ip-in-ip/output new file mode 100644 index 0000000000..7ed712aec8 --- /dev/null +++ b/testing/btest/Baseline/core.tunnels.ip-in-ip/output @@ -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]] diff --git a/testing/btest/Traces/tunnels/4in4.pcap b/testing/btest/Traces/tunnels/4in4.pcap new file mode 100644 index 0000000000..b0d89eedda Binary files /dev/null and b/testing/btest/Traces/tunnels/4in4.pcap differ diff --git a/testing/btest/Traces/tunnels/4in6.pcap b/testing/btest/Traces/tunnels/4in6.pcap new file mode 100644 index 0000000000..5c813b9b75 Binary files /dev/null and b/testing/btest/Traces/tunnels/4in6.pcap differ diff --git a/testing/btest/Traces/tunnels/6in4.pcap b/testing/btest/Traces/tunnels/6in4.pcap new file mode 100644 index 0000000000..2d0cd5c8c7 Binary files /dev/null and b/testing/btest/Traces/tunnels/6in4.pcap differ diff --git a/testing/btest/Traces/tunnels/6in6-tunnel-change.pcap b/testing/btest/Traces/tunnels/6in6-tunnel-change.pcap new file mode 100644 index 0000000000..c5838fd136 Binary files /dev/null and b/testing/btest/Traces/tunnels/6in6-tunnel-change.pcap differ diff --git a/testing/btest/Traces/tunnels/6in6.pcap b/testing/btest/Traces/tunnels/6in6.pcap new file mode 100644 index 0000000000..ff8aa607bb Binary files /dev/null and b/testing/btest/Traces/tunnels/6in6.pcap differ diff --git a/testing/btest/Traces/tunnels/6in6in6.pcap b/testing/btest/Traces/tunnels/6in6in6.pcap new file mode 100644 index 0000000000..192524aa78 Binary files /dev/null and b/testing/btest/Traces/tunnels/6in6in6.pcap differ diff --git a/testing/btest/core/tunnels/ip-in-ip.test b/testing/btest/core/tunnels/ip-in-ip.test new file mode 100644 index 0000000000..f526575d48 --- /dev/null +++ b/testing/btest/core/tunnels/ip-in-ip.test @@ -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); + }