Add new ICMP packet analyzer, remove old one

This commit is contained in:
Tim Wojtulewicz 2021-04-14 09:43:59 -07:00
parent c1f0d312b5
commit d8adfaef65
14 changed files with 933 additions and 1057 deletions

View file

@ -3,6 +3,7 @@ include(ZeekPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(PacketAnalyzer ICMP_PKT)
zeek_plugin_begin(Zeek ICMP)
zeek_plugin_cc(ICMP.cc Plugin.cc)
zeek_plugin_bif(events.bif)
zeek_plugin_end()

View file

@ -5,13 +5,30 @@
#include <netinet/icmp6.h>
#include "zeek/RunState.h"
#include "zeek/Conn.h"
#include "zeek/Reporter.h"
#include "zeek/Desc.h"
#include "zeek/Val.h"
#include "zeek/analyzer/Manager.h"
#include "zeek/session/Manager.h"
#include "zeek/analyzer/protocol/conn-size/ConnSize.h"
#include "zeek/ZeekString.h"
#include "zeek/packet_analysis/protocol/icmp/events.bif.h"
enum ICMP_EndpointState {
ICMP_INACTIVE, // no packet seen
ICMP_ACTIVE, // packets seen
};
using namespace zeek::packet_analysis::ICMP;
using namespace zeek::packet_analysis::IP;
ICMPAnalyzer::ICMPAnalyzer() : IPBasedAnalyzer("ICMP", TRANSPORT_ICMP, ICMP_PORT_MASK, false)
{
// TODO: remove once the other plugins are done
new_plugin = true;
}
ICMPAnalyzer::~ICMPAnalyzer()
@ -46,13 +63,742 @@ bool ICMPAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* packet
void ICMPAnalyzer::ContinueProcessing(Connection* c, double t, bool is_orig, int remaining, Packet* pkt)
{
auto* ta = static_cast<ICMPTransportAnalyzer*>(c->GetRootAnalyzer());
const u_char* data = pkt->ip_hdr->Payload();
int len = pkt->ip_hdr->PayloadLen();
if ( packet_contents && len > 0 )
ta->PacketContents(data + 8, std::min(len, remaining) - 8);
const struct icmp* icmpp = (const struct icmp*) data;
const std::unique_ptr<IP_Hdr>& ip = pkt->ip_hdr;
if ( ! zeek::detail::ignore_checksums &&
! zeek::id::find_val<TableVal>("ignore_checksums_nets")->Contains(ip->IPHeaderSrcAddr()) &&
remaining >= len )
{
int chksum = 0;
switch ( ip->NextProto() )
{
case IPPROTO_ICMP:
chksum = icmp_checksum(icmpp, len);
break;
case IPPROTO_ICMPV6:
chksum = icmp6_checksum(icmpp, ip.get(), len);
break;
default:
reporter->Error("unexpected IP proto in ICMP analyzer: %d", ip->NextProto());
return;
}
if ( chksum != 0xffff )
{
ta->Weird("bad_ICMP_checksum");
return;
}
}
c->SetLastTime(run_state::current_timestamp);
ta->InitEndpointMatcher(ip.get(), len, is_orig);
// Move past common portion of ICMP header.
data += 8;
remaining -= 8;
len -= 8;
ta->UpdateLength(is_orig, len);
if ( ip->NextProto() == IPPROTO_ICMP )
NextICMP4(run_state::current_timestamp, icmpp, len, remaining, data, ip.get(), ta);
else if ( ip->NextProto() == IPPROTO_ICMPV6 )
NextICMP6(run_state::current_timestamp, icmpp, len, remaining, data, ip.get(), ta);
else
{
reporter->Error("expected ICMP as IP packet's protocol, got %d", ip->NextProto());
return;
}
ForwardPacket(len, data, pkt);
if ( remaining >= len )
ta->ForwardPacket(len, data, is_orig, -1, ip.get(), remaining);
ta->MatchEndpoint(data, len, is_orig);
}
void ICMPAnalyzer::CreateTransportAnalyzer(Connection* conn, IPBasedTransportAnalyzer*& root,
analyzer::pia::PIA*& pia, bool& check_port)
void ICMPAnalyzer::NextICMP4(double t, const struct icmp* icmpp, int len, int caplen,
const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer)
{
switch ( icmpp->icmp_type )
{
case ICMP_ECHO:
case ICMP_ECHOREPLY:
Echo(t, icmpp, len, caplen, data, ip_hdr, analyzer);
break;
case ICMP_UNREACH:
case ICMP_TIMXCEED:
Context4(t, icmpp, len, caplen, data, ip_hdr, analyzer);
break;
default:
ICMP_Sent(icmpp, len, caplen, 0, data, ip_hdr, analyzer);
break;
}
}
void ICMPAnalyzer::NextICMP6(double t, const struct icmp* icmpp, int len, int caplen,
const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer)
{
switch ( icmpp->icmp_type )
{
// Echo types.
case ICMP6_ECHO_REQUEST:
case ICMP6_ECHO_REPLY:
Echo(t, icmpp, len, caplen, data, ip_hdr, analyzer);
break;
// Error messages all have the same structure for their context,
// and are handled by the same function.
case ICMP6_PARAM_PROB:
case ICMP6_TIME_EXCEEDED:
case ICMP6_PACKET_TOO_BIG:
case ICMP6_DST_UNREACH:
Context6(t, icmpp, len, caplen, data, ip_hdr, analyzer);
break;
// Router related messages.
case ND_REDIRECT:
Redirect(t, icmpp, len, caplen, data, ip_hdr, analyzer);
break;
case ND_ROUTER_ADVERT:
RouterAdvert(t, icmpp, len, caplen, data, ip_hdr, analyzer);
break;
case ND_NEIGHBOR_ADVERT:
NeighborAdvert(t, icmpp, len, caplen, data, ip_hdr, analyzer);
break;
case ND_NEIGHBOR_SOLICIT:
NeighborSolicit(t, icmpp, len, caplen, data, ip_hdr, analyzer);
break;
case ND_ROUTER_SOLICIT:
RouterSolicit(t, icmpp, len, caplen, data, ip_hdr, analyzer);
break;
case ICMP6_ROUTER_RENUMBERING:
ICMP_Sent(icmpp, len, caplen, 1, data, ip_hdr, analyzer);
break;
#if 0
// Currently not specifically implemented.
case MLD_LISTENER_QUERY:
case MLD_LISTENER_REPORT:
case MLD_LISTENER_REDUCTION:
#endif
default:
// Error messages (i.e., ICMPv6 type < 128) all have
// the same structure for their context, and are
// handled by the same function.
if ( icmpp->icmp_type < 128 )
Context6(t, icmpp, len, caplen, data, ip_hdr, analyzer);
else
ICMP_Sent(icmpp, len, caplen, 1, data, ip_hdr, analyzer);
break;
}
}
void ICMPAnalyzer::ICMP_Sent(const struct icmp* icmpp, int len, int caplen,
int icmpv6, const u_char* data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer)
{
if ( icmp_sent )
analyzer->EnqueueConnEvent(icmp_sent, analyzer->ConnVal(),
BuildInfo(icmpp, len, icmpv6, ip_hdr));
if ( icmp_sent_payload )
{
String* payload = new String(data, std::min(len, caplen), false);
analyzer->EnqueueConnEvent(icmp_sent_payload, analyzer->ConnVal(),
BuildInfo(icmpp, len, icmpv6, ip_hdr),
make_intrusive<StringVal>(payload));
}
}
zeek::RecordValPtr ICMPAnalyzer::BuildInfo(const struct icmp* icmpp, int len,
bool icmpv6, const IP_Hdr* ip_hdr)
{
static auto icmp_info = id::find_type<RecordType>("icmp_info");
auto rval = make_intrusive<zeek::RecordVal>(icmp_info);
rval->Assign(0, val_mgr->Bool(icmpv6));
rval->Assign(1, val_mgr->Count(icmpp->icmp_type));
rval->Assign(2, val_mgr->Count(icmpp->icmp_code));
rval->Assign(3, val_mgr->Count(len));
rval->Assign(4, val_mgr->Count(ip_hdr->TTL()));
return rval;
}
TransportProto ICMPAnalyzer::GetContextProtocol(const IP_Hdr* ip_hdr, uint32_t* src_port, uint32_t* dst_port)
{
const u_char* transport_hdr;
uint32_t ip_hdr_len = ip_hdr->HdrLen();
bool ip4 = ip_hdr->IP4_Hdr();
if ( ip4 )
transport_hdr = ((u_char *) ip_hdr->IP4_Hdr() + ip_hdr_len);
else
transport_hdr = ((u_char *) ip_hdr->IP6_Hdr() + ip_hdr_len);
TransportProto proto;
switch ( ip_hdr->NextProto() ) {
case 1: proto = TRANSPORT_ICMP; break;
case 6: proto = TRANSPORT_TCP; break;
case 17: proto = TRANSPORT_UDP; break;
case 58: proto = TRANSPORT_ICMP; break;
default: proto = TRANSPORT_UNKNOWN; break;
}
switch ( proto ) {
case TRANSPORT_ICMP:
{
const struct icmp* icmpp =
(const struct icmp *) transport_hdr;
bool is_one_way; // dummy
*src_port = ntohs(icmpp->icmp_type);
if ( ip4 )
*dst_port = ntohs(ICMP4_counterpart(icmpp->icmp_type,
icmpp->icmp_code, is_one_way));
else
*dst_port = ntohs(ICMP6_counterpart(icmpp->icmp_type,
icmpp->icmp_code, is_one_way));
break;
}
case TRANSPORT_TCP:
{
const struct tcphdr* tp =
(const struct tcphdr *) transport_hdr;
*src_port = ntohs(tp->th_sport);
*dst_port = ntohs(tp->th_dport);
break;
}
case TRANSPORT_UDP:
{
const struct udphdr* up =
(const struct udphdr *) transport_hdr;
*src_port = ntohs(up->uh_sport);
*dst_port = ntohs(up->uh_dport);
break;
}
default:
*src_port = *dst_port = ntohs(0);
break;
}
return proto;
}
zeek::RecordValPtr ICMPAnalyzer::ExtractICMP4Context(int len, const u_char*& data)
{
const IP_Hdr ip_hdr_data((const struct ip*) data, false);
const IP_Hdr* ip_hdr = &ip_hdr_data;
uint32_t ip_hdr_len = ip_hdr->HdrLen();
uint32_t ip_len, frag_offset;
TransportProto proto = TRANSPORT_UNKNOWN;
int DF, MF, bad_hdr_len, bad_checksum;
IPAddr src_addr, dst_addr;
uint32_t src_port, dst_port;
if ( len < (int)sizeof(struct ip) || ip_hdr_len > uint32_t(len) )
{
// We don't have an entire IP header.
bad_hdr_len = 1;
ip_len = frag_offset = 0;
DF = MF = bad_checksum = 0;
src_port = dst_port = 0;
}
else
{
bad_hdr_len = 0;
ip_len = ip_hdr->TotalLen();
bad_checksum = ! run_state::current_pkt->l3_checksummed &&
(detail::in_cksum(reinterpret_cast<const uint8_t*>(ip_hdr->IP4_Hdr()),
ip_hdr_len) != 0xffff);
src_addr = ip_hdr->SrcAddr();
dst_addr = ip_hdr->DstAddr();
DF = ip_hdr->DF();
MF = ip_hdr->MF();
frag_offset = ip_hdr->FragOffset();
if ( uint32_t(len) >= ip_hdr_len + 4 )
proto = GetContextProtocol(ip_hdr, &src_port, &dst_port);
else
{
// 4 above is the magic number meaning that both
// port numbers are included in the ICMP.
src_port = dst_port = 0;
bad_hdr_len = 1;
}
}
static auto icmp_context = id::find_type<RecordType>("icmp_context");
auto iprec = make_intrusive<zeek::RecordVal>(icmp_context);
auto id_val = make_intrusive<zeek::RecordVal>(id::conn_id);
id_val->Assign(0, make_intrusive<AddrVal>(src_addr));
id_val->Assign(1, val_mgr->Port(src_port, proto));
id_val->Assign(2, make_intrusive<AddrVal>(dst_addr));
id_val->Assign(3, val_mgr->Port(dst_port, proto));
iprec->Assign(0, std::move(id_val));
iprec->Assign(1, val_mgr->Count(ip_len));
iprec->Assign(2, val_mgr->Count(proto));
iprec->Assign(3, val_mgr->Count(frag_offset));
iprec->Assign(4, val_mgr->Bool(bad_hdr_len));
iprec->Assign(5, val_mgr->Bool(bad_checksum));
iprec->Assign(6, val_mgr->Bool(MF));
iprec->Assign(7, val_mgr->Bool(DF));
return iprec;
}
zeek::RecordValPtr ICMPAnalyzer::ExtractICMP6Context(int len, const u_char*& data)
{
int DF = 0, MF = 0, bad_hdr_len = 0;
TransportProto proto = TRANSPORT_UNKNOWN;
IPAddr src_addr;
IPAddr dst_addr;
uint32_t ip_len, frag_offset = 0;
uint32_t src_port, dst_port;
if ( len < (int)sizeof(struct ip6_hdr) )
{
bad_hdr_len = 1;
ip_len = 0;
src_port = dst_port = 0;
}
else
{
const IP_Hdr ip_hdr_data((const struct ip6_hdr*) data, false, len);
const IP_Hdr* ip_hdr = &ip_hdr_data;
ip_len = ip_hdr->TotalLen();
src_addr = ip_hdr->SrcAddr();
dst_addr = ip_hdr->DstAddr();
frag_offset = ip_hdr->FragOffset();
MF = ip_hdr->MF();
DF = ip_hdr->DF();
if ( uint32_t(len) >= uint32_t(ip_hdr->HdrLen() + 4) )
proto = GetContextProtocol(ip_hdr, &src_port, &dst_port);
else
{
// 4 above is the magic number meaning that both
// port numbers are included in the ICMP.
src_port = dst_port = 0;
bad_hdr_len = 1;
}
}
static auto icmp_context = id::find_type<RecordType>("icmp_context");
auto iprec = make_intrusive<zeek::RecordVal>(icmp_context);
auto id_val = make_intrusive<zeek::RecordVal>(id::conn_id);
id_val->Assign(0, make_intrusive<AddrVal>(src_addr));
id_val->Assign(1, val_mgr->Port(src_port, proto));
id_val->Assign(2, make_intrusive<AddrVal>(dst_addr));
id_val->Assign(3, val_mgr->Port(dst_port, proto));
iprec->Assign(0, std::move(id_val));
iprec->Assign(1, val_mgr->Count(ip_len));
iprec->Assign(2, val_mgr->Count(proto));
iprec->Assign(3, val_mgr->Count(frag_offset));
iprec->Assign(4, val_mgr->Bool(bad_hdr_len));
// bad_checksum is always false since IPv6 layer doesn't have a checksum.
iprec->Assign(5, val_mgr->False());
iprec->Assign(6, val_mgr->Bool(MF));
iprec->Assign(7, val_mgr->Bool(DF));
return iprec;
}
void ICMPAnalyzer::Echo(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer)
{
// For handling all Echo related ICMP messages
EventHandlerPtr f = nullptr;
if ( ip_hdr->NextProto() == IPPROTO_ICMPV6 )
f = (icmpp->icmp_type == ICMP6_ECHO_REQUEST)
? icmp_echo_request : icmp_echo_reply;
else
f = (icmpp->icmp_type == ICMP_ECHO)
? icmp_echo_request : icmp_echo_reply;
if ( ! f )
return;
int iid = ntohs(icmpp->icmp_hun.ih_idseq.icd_id);
int iseq = ntohs(icmpp->icmp_hun.ih_idseq.icd_seq);
String* payload = new String(data, caplen, false);
analyzer->EnqueueConnEvent(f,
analyzer->ConnVal(),
BuildInfo(icmpp, len, ip_hdr->NextProto() != IPPROTO_ICMP, ip_hdr),
val_mgr->Count(iid),
val_mgr->Count(iseq),
make_intrusive<StringVal>(payload)
);
}
void ICMPAnalyzer::RouterAdvert(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer)
{
EventHandlerPtr f = icmp_router_advertisement;
if ( ! f )
return;
uint32_t reachable = 0, retrans = 0;
if ( caplen >= (int)sizeof(reachable) )
memcpy(&reachable, data, sizeof(reachable));
if ( caplen >= (int)sizeof(reachable) + (int)sizeof(retrans) )
memcpy(&retrans, data + sizeof(reachable), sizeof(retrans));
int opt_offset = sizeof(reachable) + sizeof(retrans);
analyzer->EnqueueConnEvent(f,
analyzer->ConnVal(),
BuildInfo(icmpp, len, 1, ip_hdr),
val_mgr->Count(icmpp->icmp_num_addrs), // Cur Hop Limit
val_mgr->Bool(icmpp->icmp_wpa & 0x80), // Managed
val_mgr->Bool(icmpp->icmp_wpa & 0x40), // Other
val_mgr->Bool(icmpp->icmp_wpa & 0x20), // Home Agent
val_mgr->Count((icmpp->icmp_wpa & 0x18)>>3), // Pref
val_mgr->Bool(icmpp->icmp_wpa & 0x04), // Proxy
val_mgr->Count(icmpp->icmp_wpa & 0x02), // Reserved
make_intrusive<IntervalVal>((double)ntohs(icmpp->icmp_lifetime), Seconds),
make_intrusive<IntervalVal>((double)ntohl(reachable), Milliseconds),
make_intrusive<IntervalVal>((double)ntohl(retrans), Milliseconds),
BuildNDOptionsVal(caplen - opt_offset, data + opt_offset, analyzer)
);
}
void ICMPAnalyzer::NeighborAdvert(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer)
{
EventHandlerPtr f = icmp_neighbor_advertisement;
if ( ! f )
return;
IPAddr tgtaddr;
if ( caplen >= (int)sizeof(in6_addr) )
tgtaddr = IPAddr(*((const in6_addr*)data));
int opt_offset = sizeof(in6_addr);
analyzer->EnqueueConnEvent(f,
analyzer->ConnVal(),
BuildInfo(icmpp, len, 1, ip_hdr),
val_mgr->Bool(icmpp->icmp_num_addrs & 0x80), // Router
val_mgr->Bool(icmpp->icmp_num_addrs & 0x40), // Solicited
val_mgr->Bool(icmpp->icmp_num_addrs & 0x20), // Override
make_intrusive<AddrVal>(tgtaddr),
BuildNDOptionsVal(caplen - opt_offset, data + opt_offset, analyzer)
);
}
void ICMPAnalyzer::NeighborSolicit(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer)
{
EventHandlerPtr f = icmp_neighbor_solicitation;
if ( ! f )
return;
IPAddr tgtaddr;
if ( caplen >= (int)sizeof(in6_addr) )
tgtaddr = IPAddr(*((const in6_addr*)data));
int opt_offset = sizeof(in6_addr);
analyzer->EnqueueConnEvent(f,
analyzer->ConnVal(),
BuildInfo(icmpp, len, 1, ip_hdr),
make_intrusive<AddrVal>(tgtaddr),
BuildNDOptionsVal(caplen - opt_offset, data + opt_offset, analyzer)
);
}
void ICMPAnalyzer::Redirect(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer)
{
EventHandlerPtr f = icmp_redirect;
if ( ! f )
return;
IPAddr tgtaddr, dstaddr;
if ( caplen >= (int)sizeof(in6_addr) )
tgtaddr = IPAddr(*((const in6_addr*)data));
if ( caplen >= 2 * (int)sizeof(in6_addr) )
dstaddr = IPAddr(*((const in6_addr*)(data + sizeof(in6_addr))));
int opt_offset = 2 * sizeof(in6_addr);
analyzer->EnqueueConnEvent(f,
analyzer->ConnVal(),
BuildInfo(icmpp, len, 1, ip_hdr),
make_intrusive<AddrVal>(tgtaddr),
make_intrusive<AddrVal>(dstaddr),
BuildNDOptionsVal(caplen - opt_offset, data + opt_offset, analyzer)
);
}
void ICMPAnalyzer::RouterSolicit(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer)
{
EventHandlerPtr f = icmp_router_solicitation;
if ( ! f )
return;
analyzer->EnqueueConnEvent(f,
analyzer->ConnVal(),
BuildInfo(icmpp, len, 1, ip_hdr),
BuildNDOptionsVal(caplen, data, analyzer)
);
}
void ICMPAnalyzer::Context4(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer)
{
EventHandlerPtr f = nullptr;
switch ( icmpp->icmp_type )
{
case ICMP_UNREACH:
f = icmp_unreachable;
break;
case ICMP_TIMXCEED:
f = icmp_time_exceeded;
break;
}
if ( f )
analyzer->EnqueueConnEvent(f,
analyzer->ConnVal(),
BuildInfo(icmpp, len, 0, ip_hdr),
val_mgr->Count(icmpp->icmp_code),
ExtractICMP4Context(caplen, data)
);
}
void ICMPAnalyzer::Context6(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer)
{
EventHandlerPtr f = nullptr;
switch ( icmpp->icmp_type )
{
case ICMP6_DST_UNREACH:
f = icmp_unreachable;
break;
case ICMP6_PARAM_PROB:
f = icmp_parameter_problem;
break;
case ICMP6_TIME_EXCEEDED:
f = icmp_time_exceeded;
break;
case ICMP6_PACKET_TOO_BIG:
f = icmp_packet_too_big;
break;
default:
f = icmp_error_message;
break;
}
if ( f )
analyzer->EnqueueConnEvent(f,
analyzer->ConnVal(),
BuildInfo(icmpp, len, 1, ip_hdr),
val_mgr->Count(icmpp->icmp_code),
ExtractICMP6Context(caplen, data)
);
}
zeek::VectorValPtr ICMPAnalyzer::BuildNDOptionsVal(int caplen, const u_char* data,
ICMPTransportAnalyzer* analyzer)
{
static auto icmp6_nd_option_type = id::find_type<RecordType>("icmp6_nd_option");
static auto icmp6_nd_prefix_info_type = id::find_type<RecordType>("icmp6_nd_prefix_info");
auto vv = make_intrusive<zeek::VectorVal>(
id::find_type<VectorType>("icmp6_nd_options"));
while ( caplen > 0 )
{
// Must have at least type & length to continue parsing options.
if ( caplen < 2 )
{
analyzer->Weird("truncated_ICMPv6_ND_options");
break;
}
uint8_t type = *((const uint8_t*)data);
uint16_t length = *((const uint8_t*)(data + 1));
if ( length == 0 )
{
analyzer->Weird("zero_length_ICMPv6_ND_option");
break;
}
auto rv = make_intrusive<zeek::RecordVal>(icmp6_nd_option_type);
rv->Assign(0, val_mgr->Count(type));
rv->Assign(1, val_mgr->Count(length));
// Adjust length to be in units of bytes, exclude type/length fields.
length = length * 8 - 2;
data += 2;
caplen -= 2;
bool set_payload_field = false;
// Only parse out known options that are there in full.
switch ( type ) {
case 1:
case 2:
// Source/Target Link-layer Address option
{
if ( caplen >= length )
{
String* link_addr = new String(data, length, false);
rv->Assign(2, make_intrusive<StringVal>(link_addr));
}
else
set_payload_field = true;
break;
}
case 3:
// Prefix Information option
{
if ( caplen >= 30 )
{
auto info = make_intrusive<zeek::RecordVal>(icmp6_nd_prefix_info_type);
uint8_t prefix_len = *((const uint8_t*)(data));
bool L_flag = (*((const uint8_t*)(data + 1)) & 0x80) != 0;
bool A_flag = (*((const uint8_t*)(data + 1)) & 0x40) != 0;
uint32_t valid_life = *((const uint32_t*)(data + 2));
uint32_t prefer_life = *((const uint32_t*)(data + 6));
in6_addr prefix = *((const in6_addr*)(data + 14));
info->Assign(0, val_mgr->Count(prefix_len));
info->Assign(1, val_mgr->Bool(L_flag));
info->Assign(2, val_mgr->Bool(A_flag));
info->Assign(3, make_intrusive<IntervalVal>((double)ntohl(valid_life), Seconds));
info->Assign(4, make_intrusive<IntervalVal>((double)ntohl(prefer_life), Seconds));
info->Assign(5, make_intrusive<AddrVal>(IPAddr(prefix)));
rv->Assign(3, std::move(info));
}
else
set_payload_field = true;
break;
}
case 4:
// Redirected Header option
{
if ( caplen >= length )
{
const u_char* hdr = data + 6;
rv->Assign(4, ExtractICMP6Context(length - 6, hdr));
}
else
set_payload_field = true;
break;
}
case 5:
// MTU option
{
if ( caplen >= 6 )
rv->Assign(5, val_mgr->Count(ntohl(*((const uint32_t*)(data + 2)))));
else
set_payload_field = true;
break;
}
default:
{
set_payload_field = true;
break;
}
}
if ( set_payload_field )
{
String* payload = new String(data, std::min((int)length, caplen), false);
rv->Assign(6, make_intrusive<StringVal>(payload));
}
data += length;
caplen -= length;
vv->Assign(vv->Size(), std::move(rv));
}
return vv;
}
int ICMPAnalyzer::ICMP4_counterpart(int icmp_type, int icmp_code, bool& is_one_way)
{
@ -113,3 +859,84 @@ int ICMPAnalyzer::ICMP6_counterpart(int icmp_type, int icmp_code, bool& is_one_w
default: is_one_way = true; return icmp_code;
}
}
void ICMPAnalyzer::CreateTransportAnalyzer(Connection* conn, IPBasedTransportAnalyzer*& root,
analyzer::pia::PIA*& pia, bool& check_port)
{
root = new ICMPTransportAnalyzer(conn);
root->SetParent(this);
conn->SetInactivityTimeout(zeek::detail::icmp_inactivity_timeout);
pia = nullptr;
check_port = false;
}
void ICMPTransportAnalyzer::AddExtraAnalyzers(Connection* conn)
{
static analyzer::Tag analyzer_connsize = analyzer_mgr->GetComponentTag("CONNSIZE");
if ( analyzer_mgr->IsEnabled(analyzer_connsize) )
// Add ConnSize analyzer. Needs to see packets, not stream.
AddChildAnalyzer(new analyzer::conn_size::ConnSize_Analyzer(conn));
}
void ICMPTransportAnalyzer::UpdateConnVal(zeek::RecordVal* conn_val)
{
const auto& orig_endp = conn_val->GetField("orig");
const auto& resp_endp = conn_val->GetField("resp");
UpdateEndpointVal(orig_endp, true);
UpdateEndpointVal(resp_endp, false);
analyzer::Analyzer::UpdateConnVal(conn_val);
}
void ICMPTransportAnalyzer::UpdateEndpointVal(const ValPtr& endp_arg, bool is_orig)
{
Conn()->EnableStatusUpdateTimer();
int size = is_orig ? request_len : reply_len;
auto endp = endp_arg->AsRecordVal();
if ( size < 0 )
{
endp->Assign(0, val_mgr->Count(0));
endp->Assign(1, val_mgr->Count(int(ICMP_INACTIVE)));
}
else
{
endp->Assign(0, val_mgr->Count(size));
endp->Assign(1, val_mgr->Count(int(ICMP_ACTIVE)));
}
}
void ICMPTransportAnalyzer::UpdateLength(bool is_orig, int len)
{
int& len_stat = is_orig ? request_len : reply_len;
if ( len_stat < 0 )
len_stat = len;
else
len_stat += len;
}
void ICMPTransportAnalyzer::InitEndpointMatcher(const IP_Hdr* ip_hdr, int len, bool is_orig)
{
if ( zeek::detail::rule_matcher )
{
if ( ! matcher_state.MatcherInitialized(is_orig) )
matcher_state.InitEndpointMatcher(this, ip_hdr, len, is_orig, nullptr);
}
}
void ICMPTransportAnalyzer::MatchEndpoint(const u_char* data, int len, bool is_orig)
{
if ( zeek::detail::rule_matcher )
matcher_state.Match(zeek::detail::Rule::PAYLOAD, data, len, is_orig,
false, false, true);
}
void ICMPTransportAnalyzer::Done()
{
TransportLayerAnalyzer::Done();
matcher_state.FinishEndpointMatcher();
}

View file

@ -5,8 +5,19 @@
#include "zeek/packet_analysis/Analyzer.h"
#include "zeek/packet_analysis/Component.h"
#include "zeek/packet_analysis/protocol/ip/IPBasedAnalyzer.h"
#include "zeek/analyzer/Analyzer.h"
#include "zeek/RuleMatcher.h"
namespace zeek::packet_analysis::ICMP {
namespace zeek {
class VectorVal;
using VectorValPtr = IntrusivePtr<VectorVal>;
class RecordVal;
using RecordValPtr = IntrusivePtr<RecordVal>;
namespace packet_analysis::ICMP {
class ICMPTransportAnalyzer;
class ICMPAnalyzer final : public IP::IPBasedAnalyzer {
public:
@ -30,11 +41,95 @@ protected:
private:
void NextICMP4(double t, const struct icmp* icmpp, int len, int caplen,
const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer);
void NextICMP6(double t, const struct icmp* icmpp, int len, int caplen,
const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer);
void ICMP_Sent(const struct icmp* icmpp, int len, int caplen, int icmpv6,
const u_char* data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer);
void Echo(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer);
void Redirect(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer);
void RouterAdvert(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer);
void NeighborAdvert(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer);
void NeighborSolicit(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer);
void RouterSolicit(double t, const struct icmp* icmpp, int len,
int caplen, const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer);
RecordValPtr BuildInfo(const struct icmp* icmpp, int len,
bool icmpv6, const IP_Hdr* ip_hdr);
RecordValPtr ExtractICMP4Context(int len, const u_char*& data);
void Context4(double t, const struct icmp* icmpp, int len, int caplen,
const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer);
TransportProto GetContextProtocol(const IP_Hdr* ip_hdr, uint32_t* src_port,
uint32_t* dst_port);
RecordValPtr ExtractICMP6Context(int len, const u_char*& data);
void Context6(double t, const struct icmp* icmpp, int len, int caplen,
const u_char*& data, const IP_Hdr* ip_hdr,
ICMPTransportAnalyzer* analyzer);
// RFC 4861 Neighbor Discover message options
VectorValPtr BuildNDOptionsVal(int caplen, const u_char* data,
ICMPTransportAnalyzer* analyzer);
void UpdateEndpointVal(const ValPtr& endp, bool is_orig);
// Returns the counterpart type to the given type (e.g., the counterpart
// to ICMP_ECHOREPLY is ICMP_ECHO).
int ICMP4_counterpart(int icmp_type, int icmp_code, bool& is_one_way);
int ICMP6_counterpart(int icmp_type, int icmp_code, bool& is_one_way);
};
class ICMPTransportAnalyzer final : public IP::IPBasedTransportAnalyzer {
public:
ICMPTransportAnalyzer(Connection* conn) :
IP::IPBasedTransportAnalyzer("ICMP", conn) { }
static zeek::analyzer::Analyzer* Instantiate(Connection* conn)
{
return new ICMPTransportAnalyzer(conn);
}
void AddExtraAnalyzers(Connection* conn) override;
void UpdateConnVal(RecordVal* conn_val) override;
void UpdateEndpointVal(const ValPtr& endp, bool is_orig);
void UpdateLength(bool is_orig, int len);
void Done() override;
void InitEndpointMatcher(const IP_Hdr* ip_hdr, int len, bool is_orig);
void MatchEndpoint(const u_char* data, int len, bool is_orig);
private:
detail::RuleMatcherState matcher_state;
int request_len = -1;
int reply_len = -1;
};
}
} // namespace packet_analysis::ICMP
} // namespace zeek

View file

@ -12,9 +12,11 @@ public:
{
AddComponent(new zeek::packet_analysis::Component("ICMP",
zeek::packet_analysis::ICMP::ICMPAnalyzer::Instantiate));
AddComponent(new zeek::analyzer::Component("ICMP",
zeek::packet_analysis::ICMP::ICMPTransportAnalyzer::Instantiate));
zeek::plugin::Configuration config;
config.name = "Zeek::ICMP_PKT";
config.name = "Zeek::ICMP";
config.description = "Packet analyzer for ICMP";
return config;
}

View file

@ -0,0 +1,353 @@
## Generated for all ICMP messages that are not handled separately with
## dedicated ICMP events. Zeek's ICMP analyzer handles a number of ICMP messages
## directly with dedicated events. This event acts as a fallback for those it
## doesn't.
##
## See `Wikipedia
## <http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol>`__ for more
## information about the ICMP protocol.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard
## connection record *c*.
##
## info: Additional ICMP-specific information augmenting the standard
## connection record *c*.
##
## .. zeek:see:: icmp_error_message icmp_sent_payload
event icmp_sent%(c: connection, info: icmp_info%);
## The same as :zeek:see:`icmp_sent` except containing the ICMP payload.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard
## connection record *c*.
##
## info: Additional ICMP-specific information augmenting the standard
## connection record *c*.
##
## payload: The payload of the ICMP message.
##
## .. zeek:see:: icmp_error_message icmp_sent_payload
event icmp_sent_payload%(c: connection, info: icmp_info, payload: string%);
## Generated for ICMP *echo request* messages.
##
## See `Wikipedia
## <http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol>`__ for more
## information about the ICMP protocol.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard
## connection record *c*.
##
## info: Additional ICMP-specific information augmenting the standard
## connection record *c*.
##
## id: The *echo request* identifier.
##
## seq: The *echo request* sequence number.
##
## payload: The message-specific data of the packet payload, i.e., everything
## after the first 8 bytes of the ICMP header.
##
## .. zeek:see:: icmp_echo_reply
event icmp_echo_request%(c: connection, info: icmp_info, id: count, seq: count, payload: string%);
## Generated for ICMP *echo reply* messages.
##
## See `Wikipedia
## <http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol>`__ for more
## information about the ICMP protocol.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## info: Additional ICMP-specific information augmenting the standard
## connection record *c*.
##
## id: The *echo reply* identifier.
##
## seq: The *echo reply* sequence number.
##
## payload: The message-specific data of the packet payload, i.e., everything
## after the first 8 bytes of the ICMP header.
##
## .. zeek:see:: icmp_echo_request
event icmp_echo_reply%(c: connection, info: icmp_info, id: count, seq: count, payload: string%);
## Generated for all ICMPv6 error messages that are not handled
## separately with dedicated events. Zeek's ICMP analyzer handles a number
## of ICMP error messages directly with dedicated events. This event acts
## as a fallback for those it doesn't.
##
## See `Wikipedia
## <http://en.wikipedia.org/wiki/ICMPv6>`__ for more
## information about the ICMPv6 protocol.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard
## connection record *c*.
##
## info: Additional ICMP-specific information augmenting the standard
## connection record *c*.
##
## code: The ICMP code of the error message.
##
## context: A record with specifics of the original packet that the message
## refers to.
##
## .. zeek:see:: icmp_unreachable icmp_packet_too_big
## icmp_time_exceeded icmp_parameter_problem
event icmp_error_message%(c: connection, info: icmp_info, code: count, context: icmp_context%);
## Generated for ICMP *destination unreachable* messages.
##
## See `Wikipedia
## <http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol>`__ for more
## information about the ICMP protocol.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## info: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## code: The ICMP code of the *unreachable* message.
##
## context: A record with specifics of the original packet that the message
## refers to. *Unreachable* messages should include the original IP
## header from the packet that triggered them, and Zeek parses that
## into the *context* structure. Note that if the *unreachable*
## includes only a partial IP header for some reason, no
## fields of *context* will be filled out.
##
## .. zeek:see:: icmp_error_message icmp_packet_too_big
## icmp_time_exceeded icmp_parameter_problem
event icmp_unreachable%(c: connection, info: icmp_info, code: count, context: icmp_context%);
## Generated for ICMPv6 *packet too big* messages.
##
## See `Wikipedia
## <http://en.wikipedia.org/wiki/ICMPv6>`__ for more
## information about the ICMPv6 protocol.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## info: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## code: The ICMP code of the *too big* message.
##
## context: A record with specifics of the original packet that the message
## refers to. *Too big* messages should include the original IP header
## from the packet that triggered them, and Zeek parses that into
## the *context* structure. Note that if the *too big* includes only
## a partial IP header for some reason, no fields of *context* will
## be filled out.
##
## .. zeek:see:: icmp_error_message icmp_unreachable
## icmp_time_exceeded icmp_parameter_problem
event icmp_packet_too_big%(c: connection, info: icmp_info, code: count, context: icmp_context%);
## Generated for ICMP *time exceeded* messages.
##
## See `Wikipedia
## <http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol>`__ for more
## information about the ICMP protocol.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## info: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## code: The ICMP code of the *exceeded* message.
##
## context: A record with specifics of the original packet that the message
## refers to. *Unreachable* messages should include the original IP
## header from the packet that triggered them, and Zeek parses that
## into the *context* structure. Note that if the *exceeded* includes
## only a partial IP header for some reason, no fields of *context*
## will be filled out.
##
## .. zeek:see:: icmp_error_message icmp_unreachable icmp_packet_too_big
## icmp_parameter_problem
event icmp_time_exceeded%(c: connection, info: icmp_info, code: count, context: icmp_context%);
## Generated for ICMPv6 *parameter problem* messages.
##
## See `Wikipedia
## <http://en.wikipedia.org/wiki/ICMPv6>`__ for more
## information about the ICMPv6 protocol.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## info: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## code: The ICMP code of the *parameter problem* message.
##
## context: A record with specifics of the original packet that the message
## refers to. *Parameter problem* messages should include the original
## IP header from the packet that triggered them, and Zeek parses that
## into the *context* structure. Note that if the *parameter problem*
## includes only a partial IP header for some reason, no fields
## of *context* will be filled out.
##
## .. zeek:see:: icmp_error_message icmp_unreachable icmp_packet_too_big
## icmp_time_exceeded
event icmp_parameter_problem%(c: connection, info: icmp_info, code: count, context: icmp_context%);
## Generated for ICMP *router solicitation* messages.
##
## See `Wikipedia
## <http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol>`__ for more
## information about the ICMP protocol.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## info: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## options: Any Neighbor Discovery options included with message (:rfc:`4861`).
##
## .. zeek:see:: icmp_router_advertisement
## icmp_neighbor_solicitation icmp_neighbor_advertisement icmp_redirect
event icmp_router_solicitation%(c: connection, info: icmp_info, options: icmp6_nd_options%);
## Generated for ICMP *router advertisement* messages.
##
## See `Wikipedia
## <http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol>`__ for more
## information about the ICMP protocol.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## info: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## cur_hop_limit: The default value that should be placed in Hop Count field
## for outgoing IP packets.
##
## managed: Managed address configuration flag, :rfc:`4861`.
##
## other: Other stateful configuration flag, :rfc:`4861`.
##
## home_agent: Mobile IPv6 home agent flag, :rfc:`3775`.
##
## pref: Router selection preferences, :rfc:`4191`.
##
## proxy: Neighbor discovery proxy flag, :rfc:`4389`.
##
## rsv: Remaining two reserved bits of router advertisement flags.
##
## router_lifetime: How long this router should be used as a default router.
##
## reachable_time: How long a neighbor should be considered reachable.
##
## retrans_timer: How long a host should wait before retransmitting.
##
## options: Any Neighbor Discovery options included with message (:rfc:`4861`).
##
## .. zeek:see:: icmp_router_solicitation
## icmp_neighbor_solicitation icmp_neighbor_advertisement icmp_redirect
event icmp_router_advertisement%(c: connection, info: icmp_info, cur_hop_limit: count, managed: bool, other: bool, home_agent: bool, pref: count, proxy: bool, rsv: count, router_lifetime: interval, reachable_time: interval, retrans_timer: interval, options: icmp6_nd_options%);
## Generated for ICMP *neighbor solicitation* messages.
##
## See `Wikipedia
## <http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol>`__ for more
## information about the ICMP protocol.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## info: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## tgt: The IP address of the target of the solicitation.
##
## options: Any Neighbor Discovery options included with message (:rfc:`4861`).
##
## .. zeek:see:: icmp_router_solicitation icmp_router_advertisement
## icmp_neighbor_advertisement icmp_redirect
event icmp_neighbor_solicitation%(c: connection, info: icmp_info, tgt: addr, options: icmp6_nd_options%);
## Generated for ICMP *neighbor advertisement* messages.
##
## See `Wikipedia
## <http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol>`__ for more
## information about the ICMP protocol.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## info: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## router: Flag indicating the sender is a router.
##
## solicited: Flag indicating advertisement is in response to a solicitation.
##
## override: Flag indicating advertisement should override existing caches.
##
## tgt: the Target Address in the soliciting message or the address whose
## link-layer address has changed for unsolicited adverts.
##
## options: Any Neighbor Discovery options included with message (:rfc:`4861`).
##
## .. zeek:see:: icmp_router_solicitation icmp_router_advertisement
## icmp_neighbor_solicitation icmp_redirect
event icmp_neighbor_advertisement%(c: connection, info: icmp_info, router: bool, solicited: bool, override: bool, tgt: addr, options: icmp6_nd_options%);
## Generated for ICMP *redirect* messages.
##
## See `Wikipedia
## <http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol>`__ for more
## information about the ICMP protocol.
##
## c: The connection record for the corresponding ICMP flow.
##
## icmp: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## info: Additional ICMP-specific information augmenting the standard connection
## record *c*.
##
## tgt: The address that is supposed to be a better first hop to use for
## ICMP Destination Address.
##
## dest: The address of the destination which is redirected to the target.
##
## options: Any Neighbor Discovery options included with message (:rfc:`4861`).
##
## .. zeek:see:: icmp_router_solicitation icmp_router_advertisement
## icmp_neighbor_solicitation icmp_neighbor_advertisement
event icmp_redirect%(c: connection, info: icmp_info, tgt: addr, dest: addr, options: icmp6_nd_options%);