// See the file "COPYING" in the main distribution directory for copyright. #include "zeek/packet_analysis/protocol/icmp/ICMP.h" #include #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) { } SessionAdapter* ICMPAnalyzer::MakeSessionAdapter(Connection* conn) { auto* root = new ICMPSessionAdapter(conn); root->SetParent(this); conn->SetInactivityTimeout(zeek::detail::icmp_inactivity_timeout); return root; } bool ICMPAnalyzer::BuildConnTuple(size_t len, const uint8_t* data, Packet* packet, ConnTuple& tuple) { if ( ! CheckHeaderTrunc(ICMP_MINLEN, len, packet) ) return false; tuple.src_addr = packet->ip_hdr->SrcAddr(); tuple.dst_addr = packet->ip_hdr->DstAddr(); tuple.proto = TRANSPORT_ICMP; const struct icmp* icmpp = (const struct icmp *) data; tuple.src_port = htons(icmpp->icmp_type); if ( packet->proto == IPPROTO_ICMP ) tuple.dst_port = htons(ICMP4_counterpart(icmpp->icmp_type, icmpp->icmp_code, tuple.is_one_way)); else if ( packet->proto == IPPROTO_ICMPV6 ) tuple.dst_port = htons(ICMP6_counterpart(icmpp->icmp_type, icmpp->icmp_code, tuple.is_one_way)); else reporter->InternalError("Reached ICMP packet analyzer with unknown packet protocol %x", packet->proto); return true; } void ICMPAnalyzer::DeliverPacket(Connection* c, double t, bool is_orig, int remaining, Packet* pkt) { auto* adapter = static_cast(c->GetSessionAdapter()); const u_char* data = pkt->ip_hdr->Payload(); int len = pkt->ip_hdr->PayloadLen(); if ( packet_contents && len > 0 ) adapter->PacketContents(data + 8, std::min(len, remaining) - 8); const struct icmp* icmpp = (const struct icmp*) data; const std::unique_ptr& ip = pkt->ip_hdr; if ( ! zeek::detail::ignore_checksums && ! zeek::id::find_val("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 ) { adapter->Weird("bad_ICMP_checksum"); return; } } c->SetLastTime(run_state::current_timestamp); adapter->InitEndpointMatcher(ip.get(), len, is_orig); // Move past common portion of ICMP header. data += 8; remaining -= 8; len -= 8; adapter->UpdateLength(is_orig, len); if ( ip->NextProto() == IPPROTO_ICMP ) NextICMP4(run_state::current_timestamp, icmpp, len, remaining, data, ip.get(), adapter); else if ( ip->NextProto() == IPPROTO_ICMPV6 ) NextICMP6(run_state::current_timestamp, icmpp, len, remaining, data, ip.get(), adapter); else { reporter->Error("expected ICMP as IP packet's protocol, got %d", ip->NextProto()); return; } ForwardPacket(len, data, pkt); if ( remaining >= len ) adapter->ForwardPacket(len, data, is_orig, -1, ip.get(), remaining); adapter->MatchEndpoint(data, len, is_orig); } void ICMPAnalyzer::NextICMP4(double t, const struct icmp* icmpp, int len, int caplen, const u_char*& data, const IP_Hdr* ip_hdr, ICMPSessionAdapter* adapter) { switch ( icmpp->icmp_type ) { case ICMP_ECHO: case ICMP_ECHOREPLY: Echo(t, icmpp, len, caplen, data, ip_hdr, adapter); break; case ICMP_UNREACH: case ICMP_TIMXCEED: Context4(t, icmpp, len, caplen, data, ip_hdr, adapter); break; default: ICMP_Sent(icmpp, len, caplen, 0, data, ip_hdr, adapter); break; } } void ICMPAnalyzer::NextICMP6(double t, const struct icmp* icmpp, int len, int caplen, const u_char*& data, const IP_Hdr* ip_hdr, ICMPSessionAdapter* adapter) { switch ( icmpp->icmp_type ) { // Echo types. case ICMP6_ECHO_REQUEST: case ICMP6_ECHO_REPLY: Echo(t, icmpp, len, caplen, data, ip_hdr, adapter); 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, adapter); break; // Router related messages. case ND_REDIRECT: Redirect(t, icmpp, len, caplen, data, ip_hdr, adapter); break; case ND_ROUTER_ADVERT: RouterAdvert(t, icmpp, len, caplen, data, ip_hdr, adapter); break; case ND_NEIGHBOR_ADVERT: NeighborAdvert(t, icmpp, len, caplen, data, ip_hdr, adapter); break; case ND_NEIGHBOR_SOLICIT: NeighborSolicit(t, icmpp, len, caplen, data, ip_hdr, adapter); break; case ND_ROUTER_SOLICIT: RouterSolicit(t, icmpp, len, caplen, data, ip_hdr, adapter); break; case ICMP6_ROUTER_RENUMBERING: ICMP_Sent(icmpp, len, caplen, 1, data, ip_hdr, adapter); 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, adapter); else ICMP_Sent(icmpp, len, caplen, 1, data, ip_hdr, adapter); break; } } void ICMPAnalyzer::ICMP_Sent(const struct icmp* icmpp, int len, int caplen, int icmpv6, const u_char* data, const IP_Hdr* ip_hdr, ICMPSessionAdapter* adapter) { if ( icmp_sent ) adapter->EnqueueConnEvent(icmp_sent, adapter->ConnVal(), BuildInfo(icmpp, len, icmpv6, ip_hdr)); if ( icmp_sent_payload ) { String* payload = new String(data, std::min(len, caplen), false); adapter->EnqueueConnEvent(icmp_sent_payload, adapter->ConnVal(), BuildInfo(icmpp, len, icmpv6, ip_hdr), make_intrusive(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("icmp_info"); auto rval = make_intrusive(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(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("icmp_context"); auto iprec = make_intrusive(icmp_context); auto id_val = make_intrusive(id::conn_id); id_val->Assign(0, make_intrusive(src_addr)); id_val->Assign(1, val_mgr->Port(src_port, proto)); id_val->Assign(2, make_intrusive(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("icmp_context"); auto iprec = make_intrusive(icmp_context); auto id_val = make_intrusive(id::conn_id); id_val->Assign(0, make_intrusive(src_addr)); id_val->Assign(1, val_mgr->Port(src_port, proto)); id_val->Assign(2, make_intrusive(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, ICMPSessionAdapter* adapter) { // 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); adapter->EnqueueConnEvent(f, adapter->ConnVal(), BuildInfo(icmpp, len, ip_hdr->NextProto() != IPPROTO_ICMP, ip_hdr), val_mgr->Count(iid), val_mgr->Count(iseq), make_intrusive(payload) ); } void ICMPAnalyzer::RouterAdvert(double t, const struct icmp* icmpp, int len, int caplen, const u_char*& data, const IP_Hdr* ip_hdr, ICMPSessionAdapter* adapter) { 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); adapter->EnqueueConnEvent(f, adapter->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((double)ntohs(icmpp->icmp_lifetime), Seconds), make_intrusive((double)ntohl(reachable), Milliseconds), make_intrusive((double)ntohl(retrans), Milliseconds), BuildNDOptionsVal(caplen - opt_offset, data + opt_offset, adapter) ); } void ICMPAnalyzer::NeighborAdvert(double t, const struct icmp* icmpp, int len, int caplen, const u_char*& data, const IP_Hdr* ip_hdr, ICMPSessionAdapter* adapter) { 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); adapter->EnqueueConnEvent(f, adapter->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(tgtaddr), BuildNDOptionsVal(caplen - opt_offset, data + opt_offset, adapter) ); } void ICMPAnalyzer::NeighborSolicit(double t, const struct icmp* icmpp, int len, int caplen, const u_char*& data, const IP_Hdr* ip_hdr, ICMPSessionAdapter* adapter) { 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); adapter->EnqueueConnEvent(f, adapter->ConnVal(), BuildInfo(icmpp, len, 1, ip_hdr), make_intrusive(tgtaddr), BuildNDOptionsVal(caplen - opt_offset, data + opt_offset, adapter) ); } void ICMPAnalyzer::Redirect(double t, const struct icmp* icmpp, int len, int caplen, const u_char*& data, const IP_Hdr* ip_hdr, ICMPSessionAdapter* adapter) { 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); adapter->EnqueueConnEvent(f, adapter->ConnVal(), BuildInfo(icmpp, len, 1, ip_hdr), make_intrusive(tgtaddr), make_intrusive(dstaddr), BuildNDOptionsVal(caplen - opt_offset, data + opt_offset, adapter) ); } void ICMPAnalyzer::RouterSolicit(double t, const struct icmp* icmpp, int len, int caplen, const u_char*& data, const IP_Hdr* ip_hdr, ICMPSessionAdapter* adapter) { EventHandlerPtr f = icmp_router_solicitation; if ( ! f ) return; adapter->EnqueueConnEvent(f, adapter->ConnVal(), BuildInfo(icmpp, len, 1, ip_hdr), BuildNDOptionsVal(caplen, data, adapter) ); } void ICMPAnalyzer::Context4(double t, const struct icmp* icmpp, int len, int caplen, const u_char*& data, const IP_Hdr* ip_hdr, ICMPSessionAdapter* adapter) { EventHandlerPtr f = nullptr; switch ( icmpp->icmp_type ) { case ICMP_UNREACH: f = icmp_unreachable; break; case ICMP_TIMXCEED: f = icmp_time_exceeded; break; } if ( f ) adapter->EnqueueConnEvent(f, adapter->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, ICMPSessionAdapter* adapter) { 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 ) adapter->EnqueueConnEvent(f, adapter->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, ICMPSessionAdapter* adapter) { static auto icmp6_nd_option_type = id::find_type("icmp6_nd_option"); static auto icmp6_nd_prefix_info_type = id::find_type("icmp6_nd_prefix_info"); auto vv = make_intrusive( id::find_type("icmp6_nd_options")); while ( caplen > 0 ) { // Must have at least type & length to continue parsing options. if ( caplen < 2 ) { adapter->Weird("truncated_ICMPv6_ND_options"); break; } uint8_t type = *((const uint8_t*)data); uint16_t length = *((const uint8_t*)(data + 1)); if ( length == 0 ) { adapter->Weird("zero_length_ICMPv6_ND_option"); break; } auto rv = make_intrusive(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(link_addr)); } else set_payload_field = true; break; } case 3: // Prefix Information option { if ( caplen >= 30 ) { auto info = make_intrusive(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((double)ntohl(valid_life), Seconds)); info->Assign(4, make_intrusive((double)ntohl(prefer_life), Seconds)); info->Assign(5, make_intrusive(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(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) { is_one_way = false; // Return the counterpart type if one exists. This allows us // to track corresponding ICMP requests/replies. // Note that for the two-way ICMP messages, icmp_code is // always 0 (RFC 792). switch ( icmp_type ) { case ICMP_ECHO: return ICMP_ECHOREPLY; case ICMP_ECHOREPLY: return ICMP_ECHO; case ICMP_TSTAMP: return ICMP_TSTAMPREPLY; case ICMP_TSTAMPREPLY: return ICMP_TSTAMP; case ICMP_IREQ: return ICMP_IREQREPLY; case ICMP_IREQREPLY: return ICMP_IREQ; case ICMP_ROUTERSOLICIT: return ICMP_ROUTERADVERT; case ICMP_ROUTERADVERT: return ICMP_ROUTERSOLICIT; case ICMP_MASKREQ: return ICMP_MASKREPLY; case ICMP_MASKREPLY: return ICMP_MASKREQ; default: is_one_way = true; return icmp_code; } } int ICMPAnalyzer::ICMP6_counterpart(int icmp_type, int icmp_code, bool& is_one_way) { is_one_way = false; switch ( icmp_type ) { case ICMP6_ECHO_REQUEST: return ICMP6_ECHO_REPLY; case ICMP6_ECHO_REPLY: return ICMP6_ECHO_REQUEST; case ND_ROUTER_SOLICIT: return ND_ROUTER_ADVERT; case ND_ROUTER_ADVERT: return ND_ROUTER_SOLICIT; case ND_NEIGHBOR_SOLICIT: return ND_NEIGHBOR_ADVERT; case ND_NEIGHBOR_ADVERT: return ND_NEIGHBOR_SOLICIT; case MLD_LISTENER_QUERY: return MLD_LISTENER_REPORT; case MLD_LISTENER_REPORT: return MLD_LISTENER_QUERY; // ICMP node information query and response respectively (not defined in // icmp6.h) case 139: return 140; case 140: return 139; // Home Agent Address Discovery Request Message and reply case 144: return 145; case 145: return 144; // TODO: Add further counterparts. default: is_one_way = true; return icmp_code; } } void ICMPSessionAdapter::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 ICMPSessionAdapter::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 ICMPSessionAdapter::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 ICMPSessionAdapter::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 ICMPSessionAdapter::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 ICMPSessionAdapter::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 ICMPSessionAdapter::Done() { SessionAdapter::Done(); matcher_state.FinishEndpointMatcher(); }