diff --git a/src/packet_analysis/Analyzer.cc b/src/packet_analysis/Analyzer.cc index 497510786f..3e756d5da7 100644 --- a/src/packet_analysis/Analyzer.cc +++ b/src/packet_analysis/Analyzer.cc @@ -77,21 +77,34 @@ bool Analyzer::ForwardPacket(size_t len, const uint8_t* data, Packet* packet, uint32_t identifier) const { auto inner_analyzer = Lookup(identifier); + if ( ! inner_analyzer ) + { + for ( const auto& child : analyzers_to_detect ) + { + if ( child->DetectProtocol(len, data, packet) ) + { + DBG_LOG(DBG_PACKET_ANALYSIS, + "Protocol detection in %s succeeded, next layer analyzer is %s", + GetAnalyzerName(), child->GetAnalyzerName()); + inner_analyzer = child; + break; + } + } + } + if ( ! inner_analyzer ) inner_analyzer = default_analyzer; - if ( inner_analyzer == nullptr ) + if ( ! inner_analyzer ) { + DBG_LOG(DBG_PACKET_ANALYSIS, + "Analysis in %s failed, could not find analyzer for identifier %#x.", + GetAnalyzerName(), identifier); + if ( report_unknown_protocols ) - { - DBG_LOG(DBG_PACKET_ANALYSIS, - "Analysis in %s failed, could not find analyzer for identifier %#x.", - GetAnalyzerName(), identifier); packet_mgr->ReportUnknownProtocol(GetAnalyzerName(), identifier, data, len); - return false; - } - else - return true; + + return false; } DBG_LOG(DBG_PACKET_ANALYSIS, "Analysis in %s succeeded, next layer identifier is %#x.", @@ -101,16 +114,35 @@ bool Analyzer::ForwardPacket(size_t len, const uint8_t* data, Packet* packet, bool Analyzer::ForwardPacket(size_t len, const uint8_t* data, Packet* packet) const { - if ( default_analyzer ) - return default_analyzer->AnalyzePacket(len, data, packet); + AnalyzerPtr inner_analyzer = nullptr; - DBG_LOG(DBG_PACKET_ANALYSIS, "Analysis in %s stopped, no default analyzer available.", - GetAnalyzerName()); + for ( const auto& child : analyzers_to_detect ) + { + if ( child->DetectProtocol(len, data, packet) ) + { + DBG_LOG(DBG_PACKET_ANALYSIS, + "Protocol detection in %s succeeded, next layer analyzer is %s", + GetAnalyzerName(), child->GetAnalyzerName()); + inner_analyzer = child; + break; + } + } - if ( report_unknown_protocols ) - Weird("no_suitable_analyzer_found", packet); + if ( ! inner_analyzer ) + inner_analyzer = default_analyzer; - return true; + if ( ! inner_analyzer ) + { + DBG_LOG(DBG_PACKET_ANALYSIS, "Analysis in %s stopped, no default analyzer available.", + GetAnalyzerName()); + + if ( report_unknown_protocols ) + Weird("no_suitable_analyzer_found", packet); + + return false; + } + + return inner_analyzer->AnalyzePacket(len, data, packet); } void Analyzer::DumpDebug() const diff --git a/src/packet_analysis/Analyzer.h b/src/packet_analysis/Analyzer.h index 59f37dc291..3e61bd4428 100644 --- a/src/packet_analysis/Analyzer.h +++ b/src/packet_analysis/Analyzer.h @@ -1,6 +1,8 @@ // See the file "COPYING" in the main distribution directory for copyright. #pragma once +#include + #include "zeek/Tag.h" #include "zeek/iosource/Packet.h" #include "zeek/packet_analysis/Manager.h" @@ -98,6 +100,32 @@ public: */ void RegisterProtocol(uint32_t identifier, AnalyzerPtr child); + /** + * Registers an analyzer to use for protocol detection if identifier + * matching fails. This will also be preferred over the default analyzer + * if one exists. + * + * @param child The analyzer that will be called for protocol detection. + */ + void RegisterProtocolDetection(AnalyzerPtr child) { analyzers_to_detect.insert(child); } + + /** + * Detects whether the protocol for an analyzer can be found in the packet + * data. Packet analyzers can overload this method to provide any sort of + * pattern-matching or byte-value detection against the packet data to + * determine whether the packet contains the analyzer's protocol. The + * analyzer must also register for the detection in script-land using the + * PacketAnalyzer::register_protocol_detection bif method. + * + * @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 true if the protocol is detected in the packet data. + */ + virtual bool DetectProtocol(size_t len, const uint8_t* data, Packet* packet) { return false; } + protected: friend class Manager; @@ -174,6 +202,8 @@ private: */ bool report_unknown_protocols = true; + std::set analyzers_to_detect; + void Init(const zeek::Tag& tag); }; diff --git a/src/packet_analysis/packet_analysis.bif b/src/packet_analysis/packet_analysis.bif index 8298b3946f..c3f6e18194 100644 --- a/src/packet_analysis/packet_analysis.bif +++ b/src/packet_analysis/packet_analysis.bif @@ -28,8 +28,8 @@ function register_packet_analyzer%(parent: PacketAnalyzer::Tag, identifier: coun return zeek::val_mgr->True(); %} -## Attempts to add an entry to `parent`'s dispatcher that maps a protocol/index to a next-stage `child` analyzer. -## This may fail if either of the two names does not respond to a known analyzer. +## Attempts to add an entry to `parent`'s dispatcher that maps a protocol/index to a next-stage `child` +## analyzer. This may fail if either of the two names does not respond to a known analyzer. ## ## parent: The parent analyzer being modified ## identifier: The identifier for the protocol being registered @@ -58,3 +58,22 @@ function PacketAnalyzer::__set_ignore_checksums_nets%(v: subnet_set%) : bool zeek::packet_analysis::IP::IPBasedAnalyzer::SetIgnoreChecksumsNets(zeek::IntrusivePtr{zeek::NewRef{}, v->AsTableVal()}); return zeek::val_mgr->True(); %} + +## Registers a child analyzer with a parent analyzer to perform packet detection when determining whether +## to forward from parent to child. +## +## parent: The parent analyzer being modified +## child: The analyzer that will use protocol detection +function register_protocol_detection%(parent: PacketAnalyzer::Tag, child: PacketAnalyzer::Tag%): bool + %{ + packet_analysis::AnalyzerPtr parent_analyzer = packet_mgr->GetAnalyzer(parent->AsEnumVal()); + if ( ! parent_analyzer ) + return zeek::val_mgr->False(); + + packet_analysis::AnalyzerPtr child_analyzer = packet_mgr->GetAnalyzer(child->AsEnumVal()); + if ( ! child_analyzer ) + return zeek::val_mgr->False(); + + parent_analyzer->RegisterProtocolDetection(child_analyzer); + return zeek::val_mgr->True(); + %}