diff --git a/CHANGES b/CHANGES index 61d358a11b..9fd013d44c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,13 @@ +2.2-105 | 2014-01-20 12:16:48 -0800 + + * Support GRE tunnel decapsulation, including enhanced GRE headers. + GRE tunnels are treated just like IP-in-IP tunnels by parsing past + the GRE header in between the delivery and payload IP packets. + Addresses BIT-867. (Jon Siwek) + + * Simplify FragReassembler memory management. (Jon Siwek) + 2.2-102 | 2014-01-20 12:00:29 -0800 * Include file information (MIME type and description) into notice diff --git a/NEWS b/NEWS index 95df6d2ec1..df0bd07f4c 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,10 @@ Dependencies New Functionality ----------------- +- Support for GRE tunnel decapsulation, including enhanced GRE + headers. GRE tunnels are treated just like IP-in-IP tunnels by + parsing past the GRE header in between the delivery and payload IP + packets. Changed Functionality --------------------- diff --git a/VERSION b/VERSION index 216e72e184..3bad35d35c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2-102 +2.2-105 diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 9f8c9f42ac..8d4899b785 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -3057,6 +3057,9 @@ export { ## Toggle whether to do GTPv1 decapsulation. const enable_gtpv1 = T &redef; + ## Toggle whether to do GRE decapsulation. + const enable_gre = T &redef; + ## With this option set, the Teredo analysis will first check to see if ## other protocol analyzers have confirmed that they think they're ## parsing the right protocol and only continue with Teredo tunnel @@ -3082,7 +3085,8 @@ export { ## may work better. const delay_gtp_confirmation = F &redef; - ## How often to cleanup internal state for inactive IP tunnels. + ## How often to cleanup internal state for inactive IP tunnels + ## (includes GRE tunnels). const ip_tunnel_timeout = 24hrs &redef; } # end export module GLOBAL; diff --git a/src/Sessions.cc b/src/Sessions.cc index 7d497adf77..f7f2f37470 100644 --- a/src/Sessions.cc +++ b/src/Sessions.cc @@ -376,6 +376,31 @@ int NetSessions::CheckConnectionTag(Connection* conn) return 1; } +static unsigned int gre_header_len(uint16 flags) + { + unsigned int len = 4; // Always has 2 byte flags and 2 byte protocol type. + + if ( flags & 0x8000 ) + // Checksum/Reserved1 present. + len += 4; + + // Not considering routing presence bit since it's deprecated ... + + if ( flags & 0x2000 ) + // Key present. + len += 4; + + if ( flags & 0x1000 ) + // Sequence present. + len += 4; + + if ( flags & 0x0080 ) + // Acknowledgement present. + len += 4; + + return len; + } + void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, const IP_Hdr* ip_hdr, const u_char* const pkt, int hdr_size, const EncapsulationStack* encapsulation) @@ -446,6 +471,8 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, } } + FragReassemblerTracker frt(this, f); + len -= ip_hdr_len; // remove IP header caplen -= ip_hdr_len; @@ -460,7 +487,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, vl->append(ip_hdr->BuildPktHdrVal()); mgr.QueueEvent(esp_packet, vl); } - Remove(f); + // Can't do more since upper-layer payloads are going to be encrypted. return; } @@ -475,7 +502,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, if ( ! ignore_checksums && mobility_header_checksum(ip_hdr) != 0xffff ) { Weird("bad_MH_checksum", hdr, pkt, encapsulation); - Remove(f); return; } @@ -489,7 +515,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, if ( ip_hdr->NextProto() != IPPROTO_NONE ) Weird("mobility_piggyback", hdr, pkt, encapsulation); - Remove(f); return; } #endif @@ -497,10 +522,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, int proto = ip_hdr->NextProto(); if ( CheckHeaderTrunc(proto, len, caplen, hdr, pkt, encapsulation) ) - { - Remove(f); return; - } const u_char* data = ip_hdr->Payload(); @@ -562,13 +584,100 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, break; } + case IPPROTO_GRE: + { + if ( ! BifConst::Tunnel::enable_gre ) + { + Weird("GRE_tunnel", ip_hdr, encapsulation); + return; + } + + uint16 flags_ver = ntohs(*((uint16*)(data + 0))); + uint16 proto_typ = ntohs(*((uint16*)(data + 2))); + int gre_version = flags_ver & 0x0007; + + if ( gre_version != 0 && gre_version != 1 ) + { + Weird(fmt("unknown_gre_version_%d", gre_version), ip_hdr, + encapsulation); + return; + } + + if ( gre_version == 0 ) + { + if ( proto_typ != 0x0800 && proto_typ != 0x86dd ) + { + // Not IPv4/IPv6 payload. + Weird(fmt("unknown_gre_protocol_%"PRIu16, proto_typ), ip_hdr, + encapsulation); + return; + } + + proto = (proto_typ == 0x0800) ? IPPROTO_IPV4 : IPPROTO_IPV6; + } + + else // gre_version == 1 + { + if ( proto_typ != 0x880b ) + { + // Enhanced GRE payload must be PPP. + Weird("egre_protocol_type", ip_hdr, encapsulation); + return; + } + } + + if ( flags_ver & 0x4000 ) + { + // RFC 2784 deprecates the variable length routing field + // specified by RFC 1701. It could be parsed here, but easiest + // to just skip for now. + Weird("gre_routing", ip_hdr, encapsulation); + return; + } + + if ( flags_ver & 0x0078 ) + { + // Expect last 4 bits of flags are reserved, undefined. + Weird("unknown_gre_flags", ip_hdr, encapsulation); + return; + } + + unsigned int gre_len = gre_header_len(flags_ver); + unsigned int ppp_len = gre_version == 1 ? 1 : 0; + + if ( len < gre_len + ppp_len || caplen < gre_len + ppp_len ) + { + Weird("truncated_GRE", ip_hdr, encapsulation); + return; + } + + if ( gre_version == 1 ) + { + int ppp_proto = *((uint8*)(data + gre_len)); + + if ( ppp_proto != 0x0021 && ppp_proto != 0x0057 ) + { + Weird("non_ip_packet_in_egre", ip_hdr, encapsulation); + return; + } + + proto = (ppp_proto == 0x0021) ? IPPROTO_IPV4 : IPPROTO_IPV6; + } + + data += gre_len + ppp_len; + len -= gre_len + ppp_len; + caplen -= gre_len + ppp_len; + + // Treat GRE tunnel like IP tunnels, fallthrough to logic below now + // that GRE header is stripped and only payload packet remains. + } + case IPPROTO_IPV4: case IPPROTO_IPV6: { if ( ! BifConst::Tunnel::enable_ip ) { Weird("IP_tunnel", ip_hdr, encapsulation); - Remove(f); return; } @@ -576,7 +685,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, encapsulation->Depth() >= BifConst::Tunnel::max_depth ) { Weird("exceeded_tunnel_max_depth", ip_hdr, encapsulation); - Remove(f); return; } @@ -593,7 +701,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, if ( result != 0 ) { delete inner; - Remove(f); return; } @@ -620,7 +727,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, DoNextInnerPacket(t, hdr, inner, encapsulation, ip_tunnels[tunnel_idx].first); - Remove(f); return; } @@ -633,13 +739,11 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, encapsulation->LastType() == BifEnum::Tunnel::TEREDO ) ) Weird("ipv6_no_next", hdr, pkt); - Remove(f); return; } default: Weird(fmt("unknown_protocol_%d", proto), hdr, pkt, encapsulation); - Remove(f); return; } @@ -665,7 +769,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, if ( consistent < 0 ) { delete h; - Remove(f); return; } @@ -689,7 +792,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, if ( ! conn ) { delete h; - Remove(f); return; } @@ -721,7 +823,6 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, { // Above we already recorded the fragment in its entirety. f->DeleteTimer(); - Remove(f); } else if ( record_packet ) @@ -822,6 +923,9 @@ bool NetSessions::CheckHeaderTrunc(int proto, uint32 len, uint32 caplen, case IPPROTO_NONE: min_hdr_len = 0; break; + case IPPROTO_GRE: + min_hdr_len = 4; + break; case IPPROTO_ICMP: case IPPROTO_ICMPV6: default: diff --git a/src/Sessions.h b/src/Sessions.h index 1788541f45..e2dec3b1aa 100644 --- a/src/Sessions.h +++ b/src/Sessions.h @@ -286,6 +286,21 @@ protected: NetSessions::IPPair tunnel_idx; }; + +class FragReassemblerTracker { +public: + FragReassemblerTracker(NetSessions* s, FragReassembler* f) + : net_sessions(s), frag_reassembler(f) + { } + + ~FragReassemblerTracker() + { net_sessions->Remove(frag_reassembler); } + +private: + NetSessions* net_sessions; + FragReassembler* frag_reassembler; +}; + // Manager for the currently active sessions. extern NetSessions* sessions; diff --git a/src/const.bif b/src/const.bif index a45dfb0a7a..fd0419c7d9 100644 --- a/src/const.bif +++ b/src/const.bif @@ -17,6 +17,7 @@ const Tunnel::enable_ip: bool; const Tunnel::enable_ayiya: bool; const Tunnel::enable_teredo: bool; const Tunnel::enable_gtpv1: bool; +const Tunnel::enable_gre: bool; const Tunnel::yielding_teredo_decapsulation: bool; const Tunnel::delay_teredo_confirmation: bool; const Tunnel::delay_gtp_confirmation: bool; diff --git a/testing/btest/Baseline/core.tunnels.gre-in-gre/conn.log b/testing/btest/Baseline/core.tunnels.gre-in-gre/conn.log new file mode 100644 index 0000000000..6da1bd2132 --- /dev/null +++ b/testing/btest/Baseline/core.tunnels.gre-in-gre/conn.log @@ -0,0 +1,12 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path conn +#open 2014-01-16-21-51-36 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state local_orig missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents +#types time string addr port addr port enum string interval count count string bool count string count count count count table[string] +1341436440.002928 CRJuHdVW0XPVINV8a 3.3.3.2 520 224.0.0.9 520 udp - 26.148268 48 0 S0 - 0 D 2 104 0 0 CjhGID4nQcgTWjvg4c +1341436424.378840 CsRx2w45OKnoww6xl4 3.3.3.1 520 224.0.0.9 520 udp - 28.555457 168 0 S0 - 0 D 2 224 0 0 CjhGID4nQcgTWjvg4c +1341436424.204043 CCvvfg3TEfuqmmG4bh 10.10.25.1 8 192.168.1.2 0 icmp - 42.380221 22464 22464 OTH - 0 - 312 31200 312 31200 CjhGID4nQcgTWjvg4c +#close 2014-01-16-21-51-36 diff --git a/testing/btest/Baseline/core.tunnels.gre-in-gre/tunnel.log b/testing/btest/Baseline/core.tunnels.gre-in-gre/tunnel.log new file mode 100644 index 0000000000..277d1df679 --- /dev/null +++ b/testing/btest/Baseline/core.tunnels.gre-in-gre/tunnel.log @@ -0,0 +1,11 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path tunnel +#open 2014-01-16-21-51-36 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p tunnel_type action +#types time string addr port addr port enum enum +1341436424.204043 CXWv6p3arKYeMETxOg 72.205.54.70 0 86.106.164.150 0 Tunnel::IP Tunnel::DISCOVER +1341436424.204043 CjhGID4nQcgTWjvg4c 10.10.11.2 0 10.10.13.2 0 Tunnel::IP Tunnel::DISCOVER +#close 2014-01-16-21-51-36 diff --git a/testing/btest/Baseline/core.tunnels.gre/conn.log b/testing/btest/Baseline/core.tunnels.gre/conn.log new file mode 100644 index 0000000000..b29d87f40a --- /dev/null +++ b/testing/btest/Baseline/core.tunnels.gre/conn.log @@ -0,0 +1,16 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path conn +#open 2014-01-16-21-51-12 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state local_orig missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents +#types time string addr port addr port enum string interval count count string bool count string count count count count table[string] +1055289978.756932 CsRx2w45OKnoww6xl4 66.59.111.190 40264 172.28.2.3 22 tcp ssh 3.157831 952 1671 SF - 0 ShAdDaFf 12 1584 10 2199 CXWv6p3arKYeMETxOg +1055289987.055189 CRJuHdVW0XPVINV8a 66.59.111.190 37675 172.28.2.3 53 udp dns 5.001141 66 0 S0 - 0 D 2 122 0 0 CXWv6p3arKYeMETxOg +1055289996.849099 CIPOse170MGiRM1Qf4 66.59.111.190 123 129.170.17.4 123 udp - 0.072374 48 48 SF - 0 Dd 1 76 1 76 CXWv6p3arKYeMETxOg +1055289973.849878 CCvvfg3TEfuqmmG4bh 66.59.111.190 123 18.26.4.105 123 udp - 0.074086 48 48 SF - 0 Dd 1 76 1 76 CXWv6p3arKYeMETxOg +1055289992.849231 C6pKV8GSxOnSLghOa 66.59.111.190 123 66.59.111.182 123 udp - 0.056629 48 48 SF - 0 Dd 1 76 1 76 CXWv6p3arKYeMETxOg +1055289968.793044 CjhGID4nQcgTWjvg4c 66.59.111.190 8 172.28.2.3 0 icmp - 3.061298 224 224 OTH - 0 - 4 336 4 336 CXWv6p3arKYeMETxOg +1055289987.106744 CPbrpk1qSsw6ESzHV4 172.28.2.3 3 66.59.111.190 3 icmp - 4.994662 122 0 OTH - 0 - 2 178 0 0 CXWv6p3arKYeMETxOg +#close 2014-01-16-21-51-12 diff --git a/testing/btest/Baseline/core.tunnels.gre/dns.log b/testing/btest/Baseline/core.tunnels.gre/dns.log new file mode 100644 index 0000000000..2b8b9fc6ab --- /dev/null +++ b/testing/btest/Baseline/core.tunnels.gre/dns.log @@ -0,0 +1,11 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path dns +#open 2014-01-16-21-51-12 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto trans_id query qclass qclass_name qtype qtype_name rcode rcode_name AA TC RD RA Z answers TTLs rejected +#types time string addr port addr port enum count string count string count string count string bool bool bool bool count vector[string] vector[interval] bool +1055289987.055189 CRJuHdVW0XPVINV8a 66.59.111.190 37675 172.28.2.3 53 udp 48554 www.gleeble.org 1 C_INTERNET 255 * - - F F T F 0 - - F +1055289992.056330 CRJuHdVW0XPVINV8a 66.59.111.190 37675 172.28.2.3 53 udp 48554 www.gleeble.org 1 C_INTERNET 255 * - - F F T F 0 - - F +#close 2014-01-16-21-51-12 diff --git a/testing/btest/Baseline/core.tunnels.gre/ssh.log b/testing/btest/Baseline/core.tunnels.gre/ssh.log new file mode 100644 index 0000000000..5b05545bd0 --- /dev/null +++ b/testing/btest/Baseline/core.tunnels.gre/ssh.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path ssh +#open 2014-01-16-21-51-12 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p status direction client server +#types time string addr port addr port string enum string string +1055289978.855137 CsRx2w45OKnoww6xl4 66.59.111.190 40264 172.28.2.3 22 failure INBOUND SSH-2.0-OpenSSH_3.6.1p1 SSH-1.99-OpenSSH_3.1p1 +#close 2014-01-16-21-51-12 diff --git a/testing/btest/Baseline/core.tunnels.gre/tunnel.log b/testing/btest/Baseline/core.tunnels.gre/tunnel.log new file mode 100644 index 0000000000..f0d87f4964 --- /dev/null +++ b/testing/btest/Baseline/core.tunnels.gre/tunnel.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path tunnel +#open 2014-01-16-21-51-12 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p tunnel_type action +#types time string addr port addr port enum enum +1055289968.793044 CXWv6p3arKYeMETxOg 172.27.1.66 0 66.59.109.137 0 Tunnel::IP Tunnel::DISCOVER +#close 2014-01-16-21-51-12 diff --git a/testing/btest/Traces/tunnels/gre-sample.pcap b/testing/btest/Traces/tunnels/gre-sample.pcap new file mode 100644 index 0000000000..31c08b6ba4 Binary files /dev/null and b/testing/btest/Traces/tunnels/gre-sample.pcap differ diff --git a/testing/btest/Traces/tunnels/gre-within-gre.pcap b/testing/btest/Traces/tunnels/gre-within-gre.pcap new file mode 100644 index 0000000000..c0a8d5c41b Binary files /dev/null and b/testing/btest/Traces/tunnels/gre-within-gre.pcap differ diff --git a/testing/btest/core/tunnels/gre-in-gre.test b/testing/btest/core/tunnels/gre-in-gre.test new file mode 100644 index 0000000000..ce85f54dbb --- /dev/null +++ b/testing/btest/core/tunnels/gre-in-gre.test @@ -0,0 +1,3 @@ +# @TEST-EXEC: bro -r $TRACES/tunnels/gre-within-gre.pcap +# @TEST-EXEC: btest-diff conn.log +# @TEST-EXEC: btest-diff tunnel.log diff --git a/testing/btest/core/tunnels/gre.test b/testing/btest/core/tunnels/gre.test new file mode 100644 index 0000000000..0ce9a0c8b8 --- /dev/null +++ b/testing/btest/core/tunnels/gre.test @@ -0,0 +1,5 @@ +# @TEST-EXEC: bro -r $TRACES/tunnels/gre-sample.pcap +# @TEST-EXEC: btest-diff conn.log +# @TEST-EXEC: btest-diff tunnel.log +# @TEST-EXEC: btest-diff dns.log +# @TEST-EXEC: btest-diff ssh.log