diff --git a/src/Conn.cc b/src/Conn.cc index 53abcc26eb..ec62a1b944 100644 --- a/src/Conn.cc +++ b/src/Conn.cc @@ -113,7 +113,7 @@ unsigned int Connection::external_connections = 0; IMPLEMENT_SERIAL(Connection, SER_CONNECTION); Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id, - uint32 flow, const Encapsulation& arg_encap) + uint32 flow, const Encapsulation* arg_encap) { sessions = s; key = k; @@ -161,7 +161,10 @@ Connection::Connection(NetSessions* s, HashKey* k, double t, const ConnID* id, uid = 0; // Will set later. - encapsulation = arg_encap; + if ( arg_encap ) + encapsulation = new Encapsulation(arg_encap); + else + encapsulation = 0; if ( conn_timer_mgr ) { @@ -190,12 +193,38 @@ Connection::~Connection() delete key; delete root_analyzer; delete conn_timer_mgr; + delete encapsulation; --current_connections; if ( conn_timer_mgr ) --external_connections; } +void Connection::CheckEncapsulation(const Encapsulation* arg_encap) + { + if ( encapsulation && arg_encap ) + { + if ( *encapsulation != *arg_encap ) + { + Event(tunnel_changed, 0, arg_encap->GetVectorVal()); + delete encapsulation; + encapsulation = new Encapsulation(arg_encap); + } + } + else if ( encapsulation ) + { + Encapsulation empty; + Event(tunnel_changed, 0, empty.GetVectorVal()); + delete encapsulation; + encapsulation = new Encapsulation(arg_encap); + } + else if ( arg_encap ) + { + Event(tunnel_changed, 0, arg_encap->GetVectorVal()); + encapsulation = new Encapsulation(arg_encap); + } + } + void Connection::Done() { finished = 1; @@ -352,8 +381,8 @@ RecordVal* Connection::BuildConnVal() char tmp[20]; conn_val->Assign(9, new StringVal(uitoa_n(uid, tmp, sizeof(tmp), 62))); - if ( encapsulation.Depth() > 0 ) - conn_val->Assign(10, encapsulation.GetVectorVal()); + if ( encapsulation && encapsulation->Depth() > 0 ) + conn_val->Assign(10, encapsulation->GetVectorVal()); } if ( root_analyzer ) diff --git a/src/Conn.h b/src/Conn.h index b7911b84fb..f2efa2971d 100644 --- a/src/Conn.h +++ b/src/Conn.h @@ -52,17 +52,10 @@ class Analyzer; class Connection : public BroObj { public: Connection(NetSessions* s, HashKey* k, double t, const ConnID* id, - uint32 flow, const Encapsulation& arg_encap); + uint32 flow, const Encapsulation* arg_encap); virtual ~Connection(); - void CheckEncapsulation(const Encapsulation& arg_encap) - { - if ( encapsulation != arg_encap ) - { - Event(tunnel_changed, 0, arg_encap.GetVectorVal()); - encapsulation = arg_encap; - } - } + 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 @@ -254,7 +247,7 @@ public: uint64 GetUID() const { return uid; } - const Encapsulation& GetEncapsulation() const + const Encapsulation* GetEncapsulation() const { return encapsulation; } void CheckFlowLabel(bool is_orig, uint32 flow_label); @@ -294,7 +287,7 @@ protected: double inactivity_timeout; RecordVal* conn_val; LoginConn* login_conn; // either nil, or this - Encapsulation encapsulation; // tunnels + const Encapsulation* encapsulation; // tunnels int suppress_event; // suppress certain events to once per conn. unsigned int installed_status_timer:1; diff --git a/src/Sessions.cc b/src/Sessions.cc index 56df65d6af..5c825218d5 100644 --- a/src/Sessions.cc +++ b/src/Sessions.cc @@ -179,8 +179,6 @@ 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 @@ -188,7 +186,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, encapsulation); + DoNextPacket(t, hdr, pkt_elem->IPHdr(), pkt, hdr_size, 0); else { @@ -213,7 +211,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, encapsulation); + DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size, 0); } else if ( ip->ip_v == 6 ) @@ -225,7 +223,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, encapsulation); + DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size, 0); } else if ( ARP_Analyzer::IsARP(pkt, hdr_size) ) @@ -347,7 +345,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, Encapsulation& encapsulation) + int hdr_size, const Encapsulation* encapsulation) { uint32 caplen = hdr->caplen - hdr_size; const struct ip* ip4 = ip_hdr->IP4_Hdr(); @@ -525,23 +523,19 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, case IPPROTO_IPV4: case IPPROTO_IPV6: { - if ( encapsulation.Depth() >= BifConst::Tunnel::max_depth ) + if ( encapsulation && + 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; + Encapsulation* outer = new Encapsulation(encapsulation); + // Look up to see if we've already seen this IP tunnel, identified + // by the pair of IP addresses, so that we can always associate the + // same UID with it. IPPair tunnel_idx; if ( ip_hdr->SrcAddr() < ip_hdr->DstAddr() ) tunnel_idx = IPPair(ip_hdr->SrcAddr(), ip_hdr->DstAddr()); @@ -555,21 +549,22 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, EncapsulatingConn ec(ip_hdr->SrcAddr(), ip_hdr->DstAddr(), BifEnum::Tunnel::IP); ip_tunnels[tunnel_idx] = ec; - encapsulation.Add(ec); + outer->Add(ec); } else - encapsulation.Add(it->second); + outer->Add(it->second); - DoNextPacket(t, &fake_hdr, inner_ip, data, 0, encapsulation); + DoNextInnerPacket(t, hdr, caplen, data, proto, outer); - delete inner_ip; + delete outer; Remove(f); return; } case IPPROTO_NONE: { - if ( encapsulation.LastType() == BifEnum::Tunnel::TEREDO ) + if ( encapsulation && + encapsulation->LastType() == BifEnum::Tunnel::TEREDO ) { // TODO: raise bubble packet event } @@ -680,6 +675,31 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, } } +void NetSessions::DoNextInnerPacket(double t, const struct pcap_pkthdr* hdr, + int caplen, const u_char* pkt, int proto, + const Encapsulation* outer_encap) + { + IP_Hdr* inner_ip = 0; + + if ( proto == IPPROTO_IPV6 ) + inner_ip = new IP_Hdr((const struct ip6_hdr*) pkt, false, caplen); + else if ( proto == IPPROTO_IPV4 ) + inner_ip = new IP_Hdr((const struct ip*) pkt, false); + else + reporter->InternalError("Bad IP protocol version in DoNextInnerPacket"); + + struct pcap_pkthdr fake_hdr; + fake_hdr.caplen = fake_hdr.len = caplen; + if ( hdr ) + fake_hdr.ts = hdr->ts; + else + fake_hdr.ts.tv_sec = fake_hdr.ts.tv_usec = 0; + + DoNextPacket(t, &fake_hdr, inner_ip, pkt, 0, outer_encap); + + delete inner_ip; + } + bool NetSessions::CheckHeaderTrunc(int proto, uint32 len, uint32 caplen, const struct pcap_pkthdr* h, const u_char* p) { @@ -1013,7 +1033,7 @@ void NetSessions::GetStats(SessionStats& s) const Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id, const u_char* data, int proto, uint32 flow_label, - const Encapsulation& encapsulation) + 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. diff --git a/src/Sessions.h b/src/Sessions.h index e2bc0d704e..45c1e0750a 100644 --- a/src/Sessions.h +++ b/src/Sessions.h @@ -136,7 +136,10 @@ public: void DoNextPacket(double t, const struct pcap_pkthdr* hdr, const IP_Hdr* ip_hdr, const u_char* const pkt, - int hdr_size, Encapsulation& encapsulation); + int hdr_size, const Encapsulation* encapsulation); + + void DoNextInnerPacket(double t, const struct pcap_pkthdr* hdr, int caplen, + const u_char* pkt, int proto, const Encapsulation* outer_encap); unsigned int ConnectionMemoryUsage(); unsigned int ConnectionMemoryUsageConnVals(); @@ -150,7 +153,7 @@ protected: Connection* NewConn(HashKey* k, double t, const ConnID* id, const u_char* data, int proto, uint32 flow_lable, - const Encapsulation& encapsulation); + const Encapsulation* encapsulation); // Check whether the tag of the current packet is consistent with // the given connection. Returns: diff --git a/src/Teredo.cc b/src/Teredo.cc index 39ecef286f..08eb7d0d2b 100644 --- a/src/Teredo.cc +++ b/src/Teredo.cc @@ -92,7 +92,9 @@ void Teredo_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, { Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen); - if ( Conn()->GetEncapsulation().Depth() >= BifConst::Tunnel::max_depth ) + const Encapsulation* e = Conn()->GetEncapsulation(); + + if ( e && e->Depth() >= BifConst::Tunnel::max_depth ) { reporter->Weird(Conn(), "tunnel_depth"); return; @@ -107,20 +109,15 @@ void Teredo_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, return; } - IP_Hdr inner_ip((const struct ip6_hdr*) te.InnerIP(), false, len); - ProtocolConfirmation(); // TODO: raise Teredo-specific events - struct pcap_pkthdr fake_hdr; - fake_hdr.caplen = fake_hdr.len = len; - fake_hdr.ts.tv_sec = fake_hdr.ts.tv_usec = 0; - - Encapsulation encap(Conn()->GetEncapsulation()); + Encapsulation* outer = new Encapsulation(e); EncapsulatingConn ec(Conn(), BifEnum::Tunnel::TEREDO); - encap.Add(ec); + outer->Add(ec); - sessions->DoNextPacket(network_time, &fake_hdr, &inner_ip, te.InnerIP(), 0, - encap); + sessions->DoNextInnerPacket(network_time, 0, len, te.InnerIP(), + IPPROTO_IPV6, outer); + delete outer; } diff --git a/src/Tunnels.h b/src/Tunnels.h index 0f9c4f4107..3365c8d0ca 100644 --- a/src/Tunnels.h +++ b/src/Tunnels.h @@ -78,6 +78,14 @@ public: conns = 0; } + Encapsulation(const Encapsulation* other) + { + if ( other && other->conns ) + conns = new vector(*(other->conns)); + else + conns = 0; + } + Encapsulation& operator=(const Encapsulation& other) { if ( this == &other ) return *this; diff --git a/src/ayiya-analyzer.pac b/src/ayiya-analyzer.pac index 1a91cb1229..2fb787a4e5 100644 --- a/src/ayiya-analyzer.pac +++ b/src/ayiya-analyzer.pac @@ -12,8 +12,9 @@ flow AYIYA_Flow function process_ayiya(pdu: PDU): bool %{ Connection *c = connection()->bro_analyzer()->Conn(); + const Encapsulation* e = c->GetEncapsulation(); - if ( c->GetEncapsulation().Depth() >= BifConst::Tunnel::max_depth ) + if ( e && e->Depth() >= BifConst::Tunnel::max_depth ) { reporter->Weird(c, "tunnel_depth"); return false; @@ -25,12 +26,8 @@ flow AYIYA_Flow return false; } - IP_Hdr* inner_ip; - if ( ${pdu.next_header} == IPPROTO_IPV6 ) - inner_ip = new IP_Hdr((const struct ip6_hdr*) ${pdu.packet}.data(), false, ${pdu.packet}.length()); - else if ( ${pdu.next_header} == IPPROTO_IPV4 ) - inner_ip = new IP_Hdr((const struct ip*) ${pdu.packet}.data(), false); - else + if ( ${pdu.next_header} != IPPROTO_IPV6 && + ${pdu.next_header} != IPPROTO_IPV4 ) { reporter->Weird(c, "ayiya_tunnel_non_ip"); return false; @@ -38,17 +35,15 @@ flow AYIYA_Flow connection()->bro_analyzer()->ProtocolConfirmation(); - struct pcap_pkthdr fake_hdr; - fake_hdr.caplen = fake_hdr.len = ${pdu.packet}.length(); - fake_hdr.ts.tv_sec = fake_hdr.ts.tv_usec = 0; - - Encapsulation encap(c->GetEncapsulation()); + Encapsulation* outer = new Encapsulation(e); EncapsulatingConn ec(c, BifEnum::Tunnel::AYIYA); - encap.Add(ec); + outer->Add(ec); - sessions->DoNextPacket(network_time(), &fake_hdr, inner_ip, ${pdu.packet}.data(), 0, encap); + sessions->DoNextInnerPacket(network_time(), 0, ${pdu.packet}.length(), + ${pdu.packet}.data(), ${pdu.next_header}, + outer); - delete inner_ip; + delete outer; return true; %}