mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
211 lines
8 KiB
C++
211 lines
8 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
#include "zeek/packet_analysis/protocol/iptunnel/IPTunnel.h"
|
|
|
|
#include <pcap.h> // For DLT_ constants
|
|
|
|
#include "zeek/Conn.h"
|
|
#include "zeek/IP.h"
|
|
#include "zeek/RunState.h"
|
|
#include "zeek/TunnelEncapsulation.h"
|
|
#include "zeek/packet_analysis/protocol/ip/IP.h"
|
|
|
|
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) {
|
|
if ( ! packet->ip_hdr ) {
|
|
reporter->InternalError("IPTunnelAnalyzer: null ip_hdr in packet");
|
|
return false;
|
|
}
|
|
|
|
if ( packet->encap && packet->encap->Depth() >= BifConst::Tunnel::max_depth ) {
|
|
Weird("exceeded_tunnel_max_depth", packet);
|
|
return false;
|
|
}
|
|
|
|
int proto = packet->proto;
|
|
int gre_version = packet->gre_version;
|
|
BifEnum::Tunnel::Type tunnel_type = packet->tunnel_type;
|
|
int gre_link_type = packet->gre_link_type;
|
|
|
|
std::shared_ptr<IP_Hdr> inner = nullptr;
|
|
|
|
if ( gre_version != 0 ) {
|
|
// Check for a valid inner packet first.
|
|
auto result = packet_analysis::IP::ParsePacket(len, data, proto, inner);
|
|
if ( result == packet_analysis::IP::ParseResult::BadProtocol )
|
|
Weird("invalid_inner_IP_version", packet);
|
|
else if ( result < packet_analysis::IP::ParseResult::CaplenTooSmall )
|
|
Weird("truncated_inner_IP", packet);
|
|
else if ( result > packet_analysis::IP::ParseResult::CaplenTooLarge )
|
|
Weird("inner_IP_payload_length_mismatch", packet);
|
|
|
|
if ( result != packet_analysis::IP::ParseResult::Ok )
|
|
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 ( packet->ip_hdr->SrcAddr() < packet->ip_hdr->DstAddr() )
|
|
tunnel_idx = IPPair(packet->ip_hdr->SrcAddr(), packet->ip_hdr->DstAddr());
|
|
else
|
|
tunnel_idx = IPPair(packet->ip_hdr->DstAddr(), packet->ip_hdr->SrcAddr());
|
|
|
|
IPTunnelMap::iterator tunnel_it = ip_tunnels.find(tunnel_idx);
|
|
|
|
if ( tunnel_it == ip_tunnels.end() ) {
|
|
EncapsulatingConn ec(packet->ip_hdr->SrcAddr(), packet->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 )
|
|
return ProcessEncapsulatedPacket(run_state::processing_start_time, packet, len, len, data, gre_link_type,
|
|
packet->encap, ip_tunnels[tunnel_idx].first);
|
|
else
|
|
return ProcessEncapsulatedPacket(run_state::processing_start_time, packet, inner, packet->encap,
|
|
ip_tunnels[tunnel_idx].first);
|
|
}
|
|
|
|
/**
|
|
* Handles a packet that contains an IP header directly after the tunnel header.
|
|
*/
|
|
bool IPTunnelAnalyzer::ProcessEncapsulatedPacket(double t, const Packet* pkt, const std::shared_ptr<IP_Hdr>& inner,
|
|
std::shared_ptr<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();
|
|
|
|
auto outer = prev ? prev : std::make_shared<EncapsulationStack>();
|
|
outer->Add(ec);
|
|
|
|
// Construct fake packet containing the inner packet so it can be processed
|
|
// like a normal one.
|
|
Packet p;
|
|
p.Init(DLT_RAW, &ts, caplen, len, data, false, "");
|
|
p.encap = outer;
|
|
|
|
// Forward the packet back to the IP analyzer.
|
|
bool return_val = ForwardPacket(len, data, &p);
|
|
|
|
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,
|
|
std::shared_ptr<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);
|
|
}
|
|
|
|
auto outer = prev ? prev : std::make_shared<EncapsulationStack>();
|
|
outer->Add(ec);
|
|
|
|
// Construct fake packet containing the inner packet so it can be processed
|
|
// like a normal one.
|
|
Packet p;
|
|
p.Init(link_type, &ts, caplen, len, data, false, "");
|
|
p.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);
|
|
|
|
return return_val;
|
|
}
|
|
|
|
std::unique_ptr<Packet> build_inner_packet(Packet* outer_pkt, int* encap_index,
|
|
std::shared_ptr<EncapsulationStack> encap_stack, uint32_t inner_cap_len,
|
|
const u_char* data, int link_type, BifEnum::Tunnel::Type tunnel_type,
|
|
const Tag& analyzer_tag) {
|
|
assert(outer_pkt->cap_len >= inner_cap_len);
|
|
assert(outer_pkt->len >= outer_pkt->cap_len - inner_cap_len);
|
|
|
|
// Compute the wire length of the inner packet based on the wire length of
|
|
// the outer and the difference in capture lengths. This ensures that for
|
|
// truncated packets the wire length of the inner packet stays intact. Wire
|
|
// length may be greater than data available for truncated packets. However,
|
|
// analyzers do validate lengths found in headers with the wire length
|
|
// of the packet and keeping it consistent avoids violations.
|
|
uint32_t consumed_len = outer_pkt->cap_len - inner_cap_len;
|
|
uint32_t inner_wire_len = outer_pkt->len - consumed_len;
|
|
|
|
auto inner_pkt = std::make_unique<Packet>(link_type, &outer_pkt->ts, inner_cap_len, inner_wire_len, data);
|
|
|
|
*encap_index = 0;
|
|
if ( outer_pkt->session ) {
|
|
EncapsulatingConn inner(static_cast<Connection*>(outer_pkt->session), tunnel_type);
|
|
|
|
if ( ! outer_pkt->encap )
|
|
outer_pkt->encap = encap_stack != nullptr ? encap_stack : std::make_shared<EncapsulationStack>();
|
|
|
|
outer_pkt->encap->Add(inner);
|
|
inner_pkt->encap = outer_pkt->encap;
|
|
*encap_index = outer_pkt->encap->Depth();
|
|
}
|
|
|
|
return inner_pkt;
|
|
}
|
|
|
|
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(std::move(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
|