Move IP and IP tunnel code from Sessions into packet analyzers

This commit is contained in:
Tim Wojtulewicz 2020-09-23 16:17:06 -07:00
parent 69da2d7b1d
commit 1cf251d1ca
53 changed files with 1226 additions and 907 deletions

View file

@ -69,14 +69,16 @@ public:
* in the header. In this case, derived classes may use ForwardPacket() to
* forward the payload to the corresponding analyzer.
*
* @param len The number of bytes passed in.
* @param len The number of bytes passed in. As we move along the chain of
* analyzers, this is the number of bytes we have left of the packet to
* process.
* @param data Pointer to the input to process.
* @param packet Object that maintains the packet's meta data.
*
* @return false if the analysis failed, else true.
*/
virtual bool AnalyzePacket(size_t len, const uint8_t* data,
Packet* packet) = 0;
Packet* packet) = 0;
/**
* Dumps out debug information to the \c analyzer debug stream.
@ -124,7 +126,7 @@ protected:
* @return false if the analysis failed, else true.
*/
bool ForwardPacket(size_t len, const uint8_t* data, Packet* packet,
uint32_t identifier) const;
uint32_t identifier) const;
/**
* Triggers default analysis of the encapsulated packet if the default analyzer

View file

@ -4,6 +4,10 @@
#include "Analyzer.h"
#include "Dispatcher.h"
#include "zeek-bif.h"
#include "Stats.h"
#include "zeek/Sessions.h"
#include "zeek/RunState.h"
using namespace zeek::packet_analysis;
@ -12,6 +16,11 @@ Manager::Manager()
{
}
Manager::~Manager()
{
delete pkt_profiler;
}
void Manager::InitPostScript()
{
// Instantiate objects for all available analyzers
@ -26,6 +35,13 @@ void Manager::InitPostScript()
analyzer->Initialize();
root_analyzer = analyzers["Root"];
static auto pkt_profile_file = id::find_val("pkt_profile_file");
if ( detail::pkt_profile_mode && detail::pkt_profile_freq > 0 && pkt_profile_file )
pkt_profiler = new detail::PacketProfiler(detail::pkt_profile_mode,
detail::pkt_profile_freq,
pkt_profile_file->AsFile());
}
void Manager::Done()
@ -69,9 +85,37 @@ void Manager::ProcessPacket(Packet* packet)
static size_t counter = 0;
DBG_LOG(DBG_PACKET_ANALYSIS, "Analyzing packet %ld, ts=%.3f...", ++counter, packet->time);
#endif
zeek::detail::SegmentProfiler prof(detail::segment_logger, "dispatching-packet");
if ( pkt_profiler )
pkt_profiler->ProfilePkt(zeek::run_state::processing_start_time, packet->cap_len);
++num_packets_processed;
bool dumped_packet = false;
if ( packet->dump_packet || zeek::detail::record_all_packets )
{
// TODO: should this stay in Session?
sessions->DumpPacket(packet);
dumped_packet = true;
}
// Start packet analysis
packet->l2_valid = root_analyzer->ForwardPacket(packet->cap_len, packet->data,
packet, packet->link_type);
if ( raw_packet )
event_mgr.Enqueue(raw_packet, packet->ToRawPktHdrVal());
// Check whether packet should be recorded based on session analysis
if ( packet->dump_packet && ! dumped_packet )
// TODO: should this stay in Session?
sessions->DumpPacket(packet);
}
bool Manager::ProcessInnerPacket(Packet* packet)
{
return root_analyzer->ForwardPacket(packet->cap_len, packet->data, packet, packet->link_type);
}
AnalyzerPtr Manager::InstantiateAnalyzer(const Tag& tag)

View file

@ -8,6 +8,8 @@
#include "iosource/Packet.h"
#include "Dispatcher.h"
ZEEK_FORWARD_DECLARE_NAMESPACED(PacketProfiler, zeek::detail);
namespace zeek {
namespace packet_analysis {
@ -24,7 +26,7 @@ public:
/**
* Destructor.
*/
~Manager() = default;
~Manager();
/**
* Second-stage initialization of the manager. This is called late
@ -69,6 +71,18 @@ public:
*/
void ProcessPacket(Packet* packet);
/**
* Process the inner packet of an encapsulation. This can be used by tunnel
* analyzers to process a inner packet from the "beginning" directly through
* the root analyzer. This short-circuits some of the additional processing
* that happens in ProcessPacket().
*
* @param packet The packet to process.
*/
bool ProcessInnerPacket(Packet* packet);
uint64_t PacketsProcessed() const { return num_packets_processed; }
private:
/**
* Instantiates a new analyzer instance.
@ -92,6 +106,9 @@ private:
std::map<std::string, AnalyzerPtr> analyzers;
AnalyzerPtr root_analyzer = nullptr;
uint64_t num_packets_processed = 0;
detail::PacketProfiler* pkt_profiler = nullptr;
};
} // namespace packet_analysis

View file

@ -15,5 +15,5 @@ add_subdirectory(linux_sll)
add_subdirectory(arp)
add_subdirectory(ip)
add_subdirectory(ipv4)
add_subdirectory(ipv6)
add_subdirectory(gre)
add_subdirectory(iptunnel)

View file

@ -3,6 +3,6 @@ include(ZeekPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(PacketAnalyzer IPv4)
zeek_plugin_cc(IPv4.cc Plugin.cc)
zeek_plugin_begin(PacketAnalyzer GRE)
zeek_plugin_cc(GRE.cc Plugin.cc)
zeek_plugin_end()

View file

@ -0,0 +1,218 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "GRE.h"
#include "zeek/Sessions.h"
#include "zeek/RunState.h"
#include "zeek/IP.h"
#include "zeek/Reporter.h"
#include "pcap.h" // For DLT_ constants
using namespace zeek::packet_analysis::GRE;
static unsigned int gre_header_len(uint16_t 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;
}
GREAnalyzer::GREAnalyzer()
: zeek::packet_analysis::Analyzer("GRE")
{
}
bool GREAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* packet)
{
EncapsulationStack* encapsulation = nullptr;
auto it = packet->key_store.find("encap");
if ( it != packet->key_store.end() )
encapsulation = std::any_cast<EncapsulationStack*>(it->second);
it = packet->key_store.find("ip_hdr");
if ( it == packet->key_store.end() )
{
reporter->InternalError("GREAnalyzer: ip_hdr not found in packet keystore");
return false;
}
IP_Hdr* ip_hdr = std::any_cast<IP_Hdr*>(it->second);
int proto = -1;
it = packet->key_store.find("proto");
if ( it != packet->key_store.end() )
proto = std::any_cast<int>(proto);
if ( ! BifConst::Tunnel::enable_gre )
{
sessions->Weird("GRE_tunnel", ip_hdr, encapsulation);
return false;
}
int gre_link_type = DLT_RAW;
uint16_t flags_ver = ntohs(*((uint16_t*)(data + 0)));
uint16_t proto_typ = ntohs(*((uint16_t*)(data + 2)));
int gre_version = flags_ver & 0x0007;
unsigned int eth_len = 0;
unsigned int gre_len = gre_header_len(flags_ver);
unsigned int ppp_len = gre_version == 1 ? 4 : 0;
unsigned int erspan_len = 0;
if ( gre_version != 0 && gre_version != 1 )
{
sessions->Weird("unknown_gre_version", ip_hdr, encapsulation,
util::fmt("%d", gre_version));
return false;
}
if ( gre_version == 0 )
{
if ( proto_typ == 0x6558 )
{
// transparent ethernet bridging
if ( len > gre_len + 14 )
{
eth_len = 14;
gre_link_type = DLT_EN10MB;
proto_typ = ntohs(*((uint16_t*)(data + gre_len + eth_len - 2)));
}
else
{
sessions->Weird("truncated_GRE", ip_hdr, encapsulation);
return false;
}
}
else if ( proto_typ == 0x88be )
{
// ERSPAN type II
if ( len > gre_len + 14 + 8 )
{
erspan_len = 8;
eth_len = 14;
gre_link_type = DLT_EN10MB;
proto_typ = ntohs(*((uint16_t*)(data + gre_len + erspan_len + eth_len - 2)));
}
else
{
sessions->Weird("truncated_GRE", ip_hdr, encapsulation);
return false;
}
}
else if ( proto_typ == 0x22eb )
{
// ERSPAN type III
if ( len > gre_len + 14 + 12 )
{
erspan_len = 12;
eth_len = 14;
gre_link_type = DLT_EN10MB;
auto flags = data + gre_len + erspan_len - 1;
bool have_opt_header = ((*flags & 0x01) == 0x01);
if ( have_opt_header )
{
if ( len > gre_len + erspan_len + 8 + eth_len )
erspan_len += 8;
else
{
sessions->Weird("truncated_GRE", ip_hdr, encapsulation);
return false;
}
}
proto_typ = ntohs(*((uint16_t*)(data + gre_len + erspan_len + eth_len - 2)));
}
else
{
sessions->Weird("truncated_GRE", ip_hdr, encapsulation);
return false;
}
}
}
else // gre_version == 1
{
if ( proto_typ != 0x880b )
{
// Enhanced GRE payload must be PPP.
sessions->Weird("egre_protocol_type", ip_hdr, encapsulation,
util::fmt("%d", proto_typ));
return false;
}
}
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.
sessions->Weird("gre_routing", ip_hdr, encapsulation);
return false;
}
if ( flags_ver & 0x0078 )
{
// Expect last 4 bits of flags are reserved, undefined.
sessions->Weird("unknown_gre_flags", ip_hdr, encapsulation);
return false;
}
if ( len < gre_len + ppp_len + eth_len + erspan_len )
{
sessions->Weird("truncated_GRE", ip_hdr, encapsulation);
return false;
}
if ( gre_version == 1 )
{
uint16_t ppp_proto = ntohs(*((uint16_t*)(data + gre_len + 2)));
if ( ppp_proto != 0x0021 && ppp_proto != 0x0057 )
{
sessions->Weird("non_ip_packet_in_encap", ip_hdr, encapsulation);
return false;
}
proto = (ppp_proto == 0x0021) ? IPPROTO_IPV4 : IPPROTO_IPV6;
}
data += gre_len + ppp_len + erspan_len;
len -= gre_len + ppp_len + erspan_len;
// Treat GRE tunnel like IP tunnels, fallthrough to logic below now
// that GRE header is stripped and only payload packet remains.
// The only thing different is the tunnel type enum value to use.
BifEnum::Tunnel::Type tunnel_type = BifEnum::Tunnel::GRE;
packet->key_store["tunnel_type"] = tunnel_type;
packet->key_store["gre_version"] = gre_version;
packet->key_store["gre_link_type"] = gre_link_type;
packet->key_store["proto"] = proto;
ForwardPacket(len, data, packet);
return true;
}

View file

@ -5,18 +5,18 @@
#include <packet_analysis/Analyzer.h>
#include <packet_analysis/Component.h>
namespace zeek::packet_analysis::IPv4 {
namespace zeek::packet_analysis::GRE {
class IPv4Analyzer : public Analyzer {
class GREAnalyzer : public Analyzer {
public:
IPv4Analyzer();
~IPv4Analyzer() override = default;
GREAnalyzer();
~GREAnalyzer() override = default;
bool AnalyzePacket(size_t len, const uint8_t* data, Packet* packet) override;
static zeek::packet_analysis::AnalyzerPtr Instantiate()
{
return std::make_shared<IPv4Analyzer>();
return std::make_shared<GREAnalyzer>();
}
};

View file

@ -1,23 +1,24 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "GRE.h"
#include "plugin/Plugin.h"
#include "IPv6.h"
#include "packet_analysis/Component.h"
namespace zeek::plugin::Zeek_IPv6 {
namespace zeek::plugin::Zeek_GRE {
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure()
{
AddComponent(new zeek::packet_analysis::Component("IPv6",
zeek::packet_analysis::IPv6::IPv6Analyzer::Instantiate));
AddComponent(new zeek::packet_analysis::Component("GRE",
zeek::packet_analysis::GRE::GREAnalyzer::Instantiate));
zeek::plugin::Configuration config;
config.name = "Zeek::IPv6";
config.description = "IPv6 packet analyzer";
config.name = "Zeek::GRE";
config.description = "GRE packet analyzer";
return config;
}
} plugin;
}

View file

@ -1,37 +1,274 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "IP.h"
#include "NetVar.h"
#include "zeek/NetVar.h"
#include "zeek/IP.h"
#include "zeek/Discard.h"
#include "zeek/PacketFilter.h"
#include "zeek/Sessions.h"
#include "zeek/RunState.h"
#include "zeek/Frag.h"
#include "zeek/Event.h"
#include "zeek/TunnelEncapsulation.h"
using namespace zeek::packet_analysis::IP;
IPAnalyzer::IPAnalyzer()
: zeek::packet_analysis::Analyzer("IP")
{
discarder = new detail::Discarder();
if ( ! discarder->IsActive() )
{
delete discarder;
discarder = nullptr;
}
}
IPAnalyzer::~IPAnalyzer()
{
}
bool IPAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* packet)
{
// Assume we're pointing at IP. Just figure out which version.
if ( sizeof(struct ip) >= len )
EncapsulationStack* encapsulation = nullptr;
auto it = packet->key_store.find("encap");
if ( it != packet->key_store.end() )
encapsulation = std::any_cast<EncapsulationStack*>(it->second);
// Check to make sure we have enough data left for an IP header to be here. Note we only
// check ipv4 here. We'll check ipv6 later once we determine we have an ipv6 header.
if ( len < sizeof(struct ip) )
{
packet->Weird("packet_analyzer_truncated_header");
packet->Weird("truncated_IP");
return false;
}
// TODO: i feel like this could be generated as we move along the header hierarchy.
// TODO: the sessions code expects that the header size does not include the ip header. Should
// this change?
packet->hdr_size = static_cast<int32_t>(data - packet->data);
// Cast the current data pointer to an IP header pointer so we can use it to get some
// data about the header.
auto ip = (const struct ip *)data;
uint32_t protocol = ip->ip_v;
auto inner_analyzer = Lookup(protocol);
if ( inner_analyzer == nullptr )
std::unique_ptr<IP_Hdr> ip_hdr = nullptr;
if ( protocol == 4 )
{
DBG_LOG(DBG_PACKET_ANALYSIS, "Analysis in %s failed, could not find analyzer for identifier %#x.",
GetAnalyzerName(), protocol);
packet->Weird("no_suitable_analyzer_found");
ip_hdr = std::make_unique<IP_Hdr>(ip, false);
packet->l3_proto = L3_IPV4;
}
else if ( protocol == 6 )
{
if ( len < sizeof(struct ip6_hdr) )
{
packet->Weird("truncated_IP");
return false;
}
ip_hdr = std::make_unique<IP_Hdr>((const struct ip6_hdr*) data, false, len);
packet->l3_proto = L3_IPV6;
}
else
{
packet->Weird("unknown_ip_version");
return false;
}
DBG_LOG(DBG_PACKET_ANALYSIS, "Analysis in %s succeeded, next layer identifier is %#x.",
GetAnalyzerName(), protocol);
return inner_analyzer->AnalyzePacket(len, data, packet);
}
const struct ip* ip4 = ip_hdr->IP4_Hdr();
// total_len is the length of the packet minus all of the headers so far, including IP
uint32_t total_len = ip_hdr->TotalLen();
if ( total_len == 0 )
{
// TCP segmentation offloading can zero out the ip_len field.
packet->Weird("ip_hdr_len_zero", encapsulation);
// Cope with the zero'd out ip_len field by using the caplen.
total_len = packet->cap_len - packet->hdr_size;
}
if ( packet->len < total_len + packet->hdr_size )
{
packet->Weird("truncated_IP", encapsulation);
return false;
}
// For both of these it is safe to pass ip_hdr because the presence
// is guaranteed for the functions that pass data to us.
uint16_t ip_hdr_len = ip_hdr->HdrLen();
if ( ip_hdr_len > total_len )
{
sessions->Weird("invalid_IP_header_size", ip_hdr.get(), encapsulation);
return false;
}
if ( ip_hdr_len > len )
{
sessions->Weird("internally_truncated_header", ip_hdr.get(), encapsulation);
return false;
}
if ( ip_hdr->IP4_Hdr() )
{
if ( ip_hdr_len < sizeof(struct ip) )
{
packet->Weird("IPv4_min_header_size");
return false;
}
}
else
{
if ( ip_hdr_len < sizeof(struct ip6_hdr) )
{
packet->Weird("IPv6_min_header_size");
return false;
}
}
// Ignore if packet matches packet filter.
detail::PacketFilter* packet_filter = sessions->GetPacketFilter(false);
if ( packet_filter && packet_filter->Match(ip_hdr.get(), total_len, len) )
return false;
if ( ! packet->l2_checksummed && ! detail::ignore_checksums && ip4 &&
detail::in_cksum(reinterpret_cast<const uint8_t*>(ip4), ip_hdr_len) != 0xffff )
{
sessions->Weird("bad_IP_checksum", packet, encapsulation);
return false;
}
if ( discarder && discarder->NextPacket(ip_hdr.get(), total_len, len) )
return false;
detail::FragReassembler* f = nullptr;
if ( ip_hdr->IsFragment() )
{
packet->dump_packet = true; // always record fragments
if ( len < total_len )
{
sessions->Weird("incompletely_captured_fragment", ip_hdr.get(), encapsulation);
// Don't try to reassemble, that's doomed.
// Discard all except the first fragment (which
// is useful in analyzing header-only traces)
if ( ip_hdr->FragOffset() != 0 )
return false;
}
else
{
f = detail::fragment_mgr->NextFragment(run_state::processing_start_time, ip_hdr.get(), packet->data + packet->hdr_size);
IP_Hdr* ih = f->ReassembledPkt();
if ( ! ih )
// It didn't reassemble into anything yet.
return true;
ip4 = ih->IP4_Hdr();
// Delete the old ip_hdr and replace it with this one.
ip_hdr.reset(ih);
len = total_len = ip_hdr->TotalLen();
ip_hdr_len = ip_hdr->HdrLen();
packet->cap_len = total_len + packet->hdr_size;
// TODO: in the old code, the data pointer is updated to point at the IP header's
// payload, so it contains all of the data when it's processed. This isn't a big
// deal for when we pass it down into the session analyzers, since that does the
// same itself. should it be updated here for the case where a fragmented packet
// is actually tunneled? is that a thing that can happen? Does updating the data
// pointer without also updating the one in packet cause any problems?
if ( ip_hdr_len > total_len )
{
sessions->Weird("invalid_IP_header_size", ip_hdr.get(), encapsulation);
return false;
}
}
}
detail::FragReassemblerTracker frt(f);
// We stop building the chain when seeing IPPROTO_ESP so if it's
// there, it's always the last.
if ( ip_hdr->LastHeader() == IPPROTO_ESP )
{
packet->dump_packet = true;
if ( esp_packet )
event_mgr.Enqueue(esp_packet, ip_hdr->ToPktHdrVal());
// Can't do more since upper-layer payloads are going to be encrypted.
return true;
}
#ifdef ENABLE_MOBILE_IPV6
// We stop building the chain when seeing IPPROTO_MOBILITY so it's always
// last if present.
if ( ip_hdr->LastHeader() == IPPROTO_MOBILITY )
{
dump_this_packet = true;
if ( ! ignore_checksums && mobility_header_checksum(ip_hdr) != 0xffff )
{
sessions->Weird("bad_MH_checksum", packet, encapsulation);
return false;
}
if ( mobile_ipv6_message )
event_mgr.Enqueue(mobile_ipv6_message, ip_hdr->ToPktHdrVal());
if ( ip_hdr->NextProto() != IPPROTO_NONE )
sessions->Weird("mobility_piggyback", packet, encapsulation);
return true;
}
#endif
// Advance the data pointer past the IP header based on the header length
data += ip_hdr_len;
len -= ip_hdr_len;
bool return_val = true;
int proto = ip_hdr->NextProto();
packet->key_store["ip_hdr"] = ip_hdr.get();
packet->key_store["proto"] = proto;
switch ( proto ) {
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_ICMP:
case IPPROTO_ICMPV6:
sessions->DoNextPacket(run_state::processing_start_time, packet, ip_hdr.get(), encapsulation);
break;
case IPPROTO_NONE:
// If the packet is encapsulated in Teredo, then it was a bubble and
// the Teredo analyzer may have raised an event for that, else we're
// not sure the reason for the No Next header in the packet.
if ( ! ( encapsulation &&
encapsulation->LastType() == BifEnum::Tunnel::TEREDO ) )
{
sessions->Weird("ipv6_no_next", packet);
return_val = false;
}
break;
default:
// For everything else, pass it on to another analyzer. If there's no one to handle that,
// it'll report a Weird.
return_val = ForwardPacket(len, data, packet, proto);
break;
}
if ( f )
{
// If this was a fragment, we need to release the pointer here so that it doesn't get
// deleted. Deleting this one will be the responsibility of the fragment tracker.
ip_hdr.release();
f->DeleteTimer();
}
return return_val;
}

View file

@ -2,15 +2,18 @@
#pragma once
#include <packet_analysis/Analyzer.h>
#include <packet_analysis/Component.h>
#include "zeek/packet_analysis/Analyzer.h"
#include "zeek/packet_analysis/Component.h"
#include "zeek/Frag.h"
ZEEK_FORWARD_DECLARE_NAMESPACED(Discarder, zeek::detail);
namespace zeek::packet_analysis::IP {
class IPAnalyzer : public Analyzer {
public:
IPAnalyzer();
~IPAnalyzer() override = default;
~IPAnalyzer() override;
bool AnalyzePacket(size_t len, const uint8_t* data, Packet* packet) override;
@ -18,6 +21,15 @@ public:
{
return std::make_shared<IPAnalyzer>();
}
private:
// Returns a reassembled packet, or nil if there are still
// some missing fragments.
zeek::detail::FragReassembler* NextFragment(double t, const IP_Hdr* ip,
const u_char* pkt);
zeek::detail::Discarder* discarder = nullptr;
};
}

View file

@ -3,6 +3,6 @@ include(ZeekPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(PacketAnalyzer IPv6)
zeek_plugin_cc(IPv6.cc Plugin.cc)
zeek_plugin_begin(PacketAnalyzer IPTunnel)
zeek_plugin_cc(IPTunnel.cc Plugin.cc)
zeek_plugin_end()

View file

@ -0,0 +1,238 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "IPTunnel.h"
#include "zeek/Sessions.h"
#include "zeek/RunState.h"
#include "zeek/IP.h"
#include "zeek/TunnelEncapsulation.h"
#include "pcap.h" // For DLT_ constants
namespace zeek::packet_analysis::IPTunnel {
IPTunnelAnalyzer* ip_tunnel_analyzer;
IPTunnelAnalyzer::IPTunnelAnalyzer()
: zeek::packet_analysis::Analyzer("IPTunnel")
{
ip_tunnel_analyzer = this;
}
bool IPTunnelAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* packet)
{
EncapsulationStack* encapsulation = nullptr;
auto it = packet->key_store.find("encap");
if ( it != packet->key_store.end() )
encapsulation = std::any_cast<EncapsulationStack*>(it->second);
it = packet->key_store.find("ip_hdr");
if ( it == packet->key_store.end() )
{
reporter->InternalError("IPTunnelAnalyzer: ip_hdr not found in packet keystore");
return false;
}
IP_Hdr* ip_hdr = std::any_cast<IP_Hdr*>(it->second);
int proto = -1;
it = packet->key_store.find("proto");
if ( it != packet->key_store.end() )
proto = std::any_cast<int>(it->second);
int gre_version = -1;
it = packet->key_store.find("gre_version");
if ( it != packet->key_store.end() )
gre_version = std::any_cast<int>(it->second);
BifEnum::Tunnel::Type tunnel_type = BifEnum::Tunnel::IP;
it = packet->key_store.find("tunnel_type");
if ( it != packet->key_store.end() )
tunnel_type = std::any_cast<BifEnum::Tunnel::Type>(it->second);
int gre_link_type = DLT_RAW;
it = packet->key_store.find("gre_link_type");
if ( it != packet->key_store.end() )
gre_link_type = std::any_cast<int>(it->second);
if ( ! BifConst::Tunnel::enable_ip )
{
sessions->Weird("IP_tunnel", ip_hdr, encapsulation);
return false;
}
if ( encapsulation &&
encapsulation->Depth() >= BifConst::Tunnel::max_depth )
{
sessions->Weird("exceeded_tunnel_max_depth", ip_hdr, encapsulation);
return false;
}
IP_Hdr* inner = nullptr;
if ( gre_version != 0 )
{
// Check for a valid inner packet first.
int result = sessions->ParseIPPacket(len, data, proto, inner);
if ( result == -2 )
sessions->Weird("invalid_inner_IP_version", ip_hdr, encapsulation);
else if ( result < 0 )
sessions->Weird("truncated_inner_IP", ip_hdr, encapsulation);
else if ( result > 0 )
sessions->Weird("inner_IP_payload_length_mismatch", ip_hdr, encapsulation);
if ( result != 0 )
{
delete inner;
return false;
}
}
// 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());
else
tunnel_idx = IPPair(ip_hdr->DstAddr(), ip_hdr->SrcAddr());
IPTunnelMap::iterator tunnel_it = ip_tunnels.find(tunnel_idx);
if ( tunnel_it == ip_tunnels.end() )
{
EncapsulatingConn ec(ip_hdr->SrcAddr(), ip_hdr->DstAddr(),
tunnel_type);
ip_tunnels[tunnel_idx] = TunnelActivity(ec, run_state::network_time);
zeek::detail::timer_mgr->Add(new detail::IPTunnelTimer(run_state::network_time, tunnel_idx, this));
}
else
tunnel_it->second.second = zeek::run_state::network_time;
if ( gre_version == 0 )
ProcessEncapsulatedPacket(run_state::processing_start_time, packet, len, len, data, gre_link_type,
encapsulation, ip_tunnels[tunnel_idx].first);
else
ProcessEncapsulatedPacket(run_state::processing_start_time, packet, inner, encapsulation,
ip_tunnels[tunnel_idx].first);
return true;
}
/**
* Handles a packet that contains an IP header directly after the tunnel header.
*/
bool IPTunnelAnalyzer::ProcessEncapsulatedPacket(double t, const Packet* pkt,
const IP_Hdr* inner, const EncapsulationStack* prev,
const EncapsulatingConn& ec)
{
uint32_t caplen, len;
caplen = len = inner->TotalLen();
pkt_timeval ts;
int link_type;
if ( pkt )
ts = pkt->ts;
else
{
ts.tv_sec = (time_t) run_state::network_time;
ts.tv_usec = (suseconds_t)
((run_state::network_time - (double)ts.tv_sec) * 1000000);
}
const u_char* data = nullptr;
if ( inner->IP4_Hdr() )
data = (const u_char*) inner->IP4_Hdr();
else
data = (const u_char*) inner->IP6_Hdr();
EncapsulationStack* outer = prev ?
new EncapsulationStack(*prev) : new EncapsulationStack();
outer->Add(ec);
// Construct fake packet for DoNextPacket
Packet p;
p.Init(DLT_RAW, &ts, caplen, len, data, false, "");
p.key_store["encap"] = outer;
p.key_store["encap_inner_ip"] = inner;
// Forward the packet back to the IP analyzer.
bool return_val = ForwardPacket(len, data, &p);
delete inner;
delete outer;
return return_val;
}
/**
* Handles a packet that contains a physical-layer header after the tunnel header.
*/
bool IPTunnelAnalyzer::ProcessEncapsulatedPacket(double t, const Packet* pkt,
uint32_t caplen, uint32_t len,
const u_char* data, int link_type,
const EncapsulationStack* prev,
const EncapsulatingConn& ec)
{
pkt_timeval ts;
if ( pkt )
ts = pkt->ts;
else
{
ts.tv_sec = (time_t) run_state::network_time;
ts.tv_usec = (suseconds_t)
((run_state::network_time - (double)ts.tv_sec) * 1000000);
}
EncapsulationStack* outer = prev ?
new EncapsulationStack(*prev) : new EncapsulationStack();
outer->Add(ec);
// Construct fake packet for DoNextPacket
Packet p;
p.Init(link_type, &ts, caplen, len, data, false, "");
p.key_store["encap"] = outer;
// Process the packet as if it was a brand new packet by passing it back
// to the packet manager.
bool return_val = packet_mgr->ProcessInnerPacket(&p);
delete outer;
return return_val;
}
namespace detail {
IPTunnelTimer::IPTunnelTimer(double t, IPTunnelAnalyzer::IPPair p, IPTunnelAnalyzer* analyzer)
: Timer(t + BifConst::Tunnel::ip_tunnel_timeout,
zeek::detail::TIMER_IP_TUNNEL_INACTIVITY),
tunnel_idx(p), analyzer(analyzer)
{
}
void IPTunnelTimer::Dispatch(double t, bool is_expire)
{
IPTunnelAnalyzer::IPTunnelMap::const_iterator it =
analyzer->ip_tunnels.find(tunnel_idx);
if ( it == analyzer->ip_tunnels.end() )
return;
double last_active = it->second.second;
double inactive_time = t > last_active ? t - last_active : 0;
if ( inactive_time >= BifConst::Tunnel::ip_tunnel_timeout )
// tunnel activity timed out, delete it from map
analyzer->ip_tunnels.erase(tunnel_idx);
else if ( ! is_expire )
// tunnel activity didn't timeout, schedule another timer
zeek::detail::timer_mgr->Add(new IPTunnelTimer(t, tunnel_idx, analyzer));
}
} // namespace detail
} // namespace zeek::packet_analysis::IPTunnel

View file

@ -0,0 +1,98 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <packet_analysis/Analyzer.h>
#include <packet_analysis/Component.h>
#include "zeek/IPAddr.h"
#include "zeek/TunnelEncapsulation.h"
namespace zeek::packet_analysis::IPTunnel {
namespace detail { class IPTunnelTimer; }
class IPTunnelAnalyzer : public Analyzer {
public:
IPTunnelAnalyzer();
~IPTunnelAnalyzer() override = default;
bool AnalyzePacket(size_t len, const uint8_t* data, Packet* packet) override;
static zeek::packet_analysis::AnalyzerPtr Instantiate()
{
return std::make_shared<IPTunnelAnalyzer>();
}
/**
* Wrapper that handles encapsulated IP packets and passes them back into
* packet analysis.
*
* @param t Network time.
* @param pkt If the outer pcap header is available, this pointer can be set
* so that the fake pcap header passed to DoNextPacket will use
* the same timeval. The caplen and len fields of the fake pcap
* header are always set to the TotalLength() of \a inner.
* @param inner Pointer to IP header wrapper of the inner packet, ownership
* of the pointer's memory is assumed by this function.
* @param prev Any previous encapsulation stack of the caller, not including
* the most-recently found depth of encapsulation.
* @param ec The most-recently found depth of encapsulation.
*/
bool ProcessEncapsulatedPacket(double t, const Packet *pkt,
const IP_Hdr* inner, const EncapsulationStack* prev,
const EncapsulatingConn& ec);
/**
* Wrapper that handles encapsulated Ethernet/IP packets and passes them back into
* packet analysis.
*
* @param t Network time.
* @param pkt If the outer pcap header is available, this pointer can be
* set so that the fake pcap header passed to DoNextPacket will use
* the same timeval.
* @param caplen number of captured bytes remaining
* @param len number of bytes remaining as claimed by outer framing
* @param data the remaining packet data
* @param link_type layer 2 link type used for initializing inner packet
* @param prev Any previous encapsulation stack of the caller, not
* including the most-recently found depth of encapsulation.
* @param ec The most-recently found depth of encapsulation.
*/
bool ProcessEncapsulatedPacket(double t, const Packet* pkt,
uint32_t caplen, uint32_t len,
const u_char* data, int link_type,
const EncapsulationStack* prev,
const EncapsulatingConn& ec);
protected:
friend class detail::IPTunnelTimer;
using IPPair = std::pair<IPAddr, IPAddr>;
using TunnelActivity = std::pair<EncapsulatingConn, double>;
using IPTunnelMap = std::map<IPPair, TunnelActivity>;
IPTunnelMap ip_tunnels;
};
namespace detail {
class IPTunnelTimer final : public zeek::detail::Timer {
public:
IPTunnelTimer(double t, IPTunnelAnalyzer::IPPair p, IPTunnelAnalyzer* analyzer);
~IPTunnelTimer() override = default;
void Dispatch(double t, bool is_expire) override;
protected:
IPTunnelAnalyzer::IPPair tunnel_idx;
IPTunnelAnalyzer* analyzer;
};
} // namespace detail
// This is temporary until the TCP and UDP analyzers are moved to be packet analyzers.
extern IPTunnelAnalyzer* ip_tunnel_analyzer;
}

View file

@ -1,21 +1,21 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "IPv4.h"
#include "IPTunnel.h"
#include "plugin/Plugin.h"
#include "packet_analysis/Component.h"
namespace zeek::plugin::Zeek_IPv4 {
namespace zeek::plugin::Zeek_IPTunnel {
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure()
{
AddComponent(new zeek::packet_analysis::Component("IPv4",
zeek::packet_analysis::IPv4::IPv4Analyzer::Instantiate));
AddComponent(new zeek::packet_analysis::Component("IPTunnel",
zeek::packet_analysis::IPTunnel::IPTunnelAnalyzer::Instantiate));
zeek::plugin::Configuration config;
config.name = "Zeek::IPv4";
config.description = "IPv4 packet analyzer";
config.name = "Zeek::IPTunnel";
config.description = "IPTunnel packet analyzer";
return config;
}

View file

@ -1,20 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "IPv4.h"
using namespace zeek::packet_analysis::IPv4;
IPv4Analyzer::IPv4Analyzer()
: zeek::packet_analysis::Analyzer("IPv4")
{
}
bool IPv4Analyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* packet)
{
packet->l3_proto = L3_IPV4;
packet->hdr_size = static_cast<uint32_t>(data - packet->data);
packet->session_analysis = true;
// Leave packet analyzer land
return true;
}

View file

@ -1,20 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "IPv6.h"
using namespace zeek::packet_analysis::IPv6;
IPv6Analyzer::IPv6Analyzer()
: zeek::packet_analysis::Analyzer("IPv6")
{
}
bool IPv6Analyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* packet)
{
packet->l3_proto = L3_IPV6;
packet->hdr_size = static_cast<uint32_t>(data - packet->data);
packet->session_analysis = true;
// Leave packet analyzer land
return true;
}

View file

@ -1,23 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <packet_analysis/Analyzer.h>
#include <packet_analysis/Component.h>
namespace zeek::packet_analysis::IPv6 {
class IPv6Analyzer : public Analyzer {
public:
IPv6Analyzer();
~IPv6Analyzer() override = default;
bool AnalyzePacket(size_t len, const uint8_t* data, Packet* packet) override;
static AnalyzerPtr Instantiate()
{
return std::make_shared<IPv6Analyzer>();
}
};
}