diff --git a/Benchmarks/CMakeLists.txt b/Benchmarks/CMakeLists.txt new file mode 100644 index 0000000000..c8658e384d --- /dev/null +++ b/Benchmarks/CMakeLists.txt @@ -0,0 +1,23 @@ + +include(FetchContent) + +if(NOT ( + (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL CMAKE_SYSTEM_PROCESSOR) + AND (NOT MINGW) + AND (NOT CMAKE_OSX_ARCHITECTURES OR (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL CMAKE_OSX_ARCHITECTURES)) + ) +) + message(WARNING "Google Benchmark backend is not supported for cross-compilation") + return() +endif() + +# Fetch Google Benchmark +FetchContent_Declare(benchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.9.0) + +# Disable testing and installation for Google Benchmark +set(BENCHMARK_ENABLE_TESTING OFF) +set(BENCHMARK_ENABLE_INSTALL OFF) +FetchContent_MakeAvailable(benchmark) + +# Add PcapPlusPlus benchmarks +add_subdirectory(Packet++Bench) diff --git a/Benchmarks/Packet++Bench/Benchmarks/PortMapperBench.cpp b/Benchmarks/Packet++Bench/Benchmarks/PortMapperBench.cpp new file mode 100644 index 0000000000..a96df37f08 --- /dev/null +++ b/Benchmarks/Packet++Bench/Benchmarks/PortMapperBench.cpp @@ -0,0 +1,99 @@ +#include +#include + +#include "ParserConfig.h" + +namespace pcpp_bench +{ + using namespace pcpp; + + namespace + { + void PortMapperStaticLookupSingle(benchmark::State& state) + { + ParserConfiguration& config = ParserConfiguration::getDefault(); + PortMapper& portMapper = config.portMapper; + + size_t totalLookups = 0; + for (auto _ : state) + { + PortPair portPair(80, 443); + auto protocol = portMapper.getProtocolByPortPair(portPair); + benchmark::DoNotOptimize(protocol); + totalLookups++; + } + + state.SetItemsProcessed(totalLookups); + } + BENCHMARK(PortMapperStaticLookupSingle); + + void PortMapperStaticLookupMatrix(benchmark::State& state) + { + ParserConfiguration& config = ParserConfiguration::getDefault(); + PortMapper& portMapper = config.portMapper; + + size_t totalLookups = 0; + for (auto _ : state) + { + PortPair portPair(80, 443); + auto matrix = portMapper.getMatchMatrix(portPair); + benchmark::DoNotOptimize(matrix); + totalLookups++; + } + state.SetItemsProcessed(totalLookups); + } + BENCHMARK(PortMapperStaticLookupMatrix); + + void PortMapperDynamicLookupSingle(benchmark::State& state) + { + ParserConfiguration& config = ParserConfiguration::getDefault(); + PortMapper& portMapper = config.portMapper; + size_t totalLookups = 0; + + // Generate random port pairs for dynamic lookups + std::vector inputPorts{ + PortPair{ 80, 65440 }, + PortPair{ 64000, 64002 }, + }; + + for (auto _ : state) + { + // Simulate dynamic port pairs by cycling through a predefined set + PortPair const& portPair = inputPorts[totalLookups % inputPorts.size()]; + + auto protocol = portMapper.getProtocolByPortPair(portPair); + benchmark::DoNotOptimize(protocol); + totalLookups++; + } + + state.SetItemsProcessed(totalLookups); + } + BENCHMARK(PortMapperDynamicLookupSingle); + + void PortMapperDynamicLookupMatrix(benchmark::State& state) + { + ParserConfiguration& config = ParserConfiguration::getDefault(); + PortMapper& portMapper = config.portMapper; + size_t totalLookups = 0; + + // Generate random port pairs for dynamic lookups + std::vector inputPorts{ + PortPair{ 80, 65440 }, + PortPair{ 64000, 64002 }, + }; + + for (auto _ : state) + { + // Simulate dynamic port pairs by cycling through a predefined set + PortPair const& portPair = inputPorts[totalLookups % inputPorts.size()]; + + auto matrix = portMapper.getMatchMatrix(portPair); + benchmark::DoNotOptimize(matrix); + totalLookups++; + } + + state.SetItemsProcessed(totalLookups); + } + BENCHMARK(PortMapperDynamicLookupMatrix); + } // namespace +} // namespace pcpp_bench diff --git a/Benchmarks/Packet++Bench/CMakeLists.txt b/Benchmarks/Packet++Bench/CMakeLists.txt new file mode 100644 index 0000000000..fe3ce1a803 --- /dev/null +++ b/Benchmarks/Packet++Bench/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.14) + +add_executable(Packet++Bench + "main.cpp" + "Benchmarks/PortMapperBench.cpp" +) + +target_link_libraries(Packet++Bench PUBLIC PcapPlusPlus::Pcap++ benchmark::benchmark) diff --git a/Benchmarks/Packet++Bench/main.cpp b/Benchmarks/Packet++Bench/main.cpp new file mode 100644 index 0000000000..cdf0678e3f --- /dev/null +++ b/Benchmarks/Packet++Bench/main.cpp @@ -0,0 +1,12 @@ + +#include + +int main(int argc, char** argv) +{ + // Initialize the benchmark library + benchmark::Initialize(&argc, argv); + + // Run all benchmarks + benchmark::RunSpecifiedBenchmarks(); + return 0; +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 7337d95568..95cbb48bb2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,7 @@ cmake_dependent_option( OFF ) option(PCAPPP_BUILD_TESTS "Build Tests" ${PCAPPP_MAIN_PROJECT}) +option(PCAPPP_BUILD_BENCHMARK "Build Benchmark Targets" OFF) option(PCAPPP_BUILD_COVERAGE "Generate Coverage Report" OFF) option(PCAPPP_BUILD_FUZZERS "Build Fuzzers binaries" OFF) option(PCAPPP_BUILD_REPRODUCIBLE "Build a reproducible version" OFF) @@ -358,6 +359,10 @@ if(PCAPPP_BUILD_TESTS OR PCAPPP_BUILD_FUZZERS OR PCAPPP_BUILD_EXAMPLES) add_subdirectory(Tests) endif() +if(PCAPPP_BUILD_BENCHMARK) + add_subdirectory(Benchmarks) +endif() + if(PCAPPP_INSTALL) # Generate PKG-Config for non WIN32 system if(NOT WIN32) diff --git a/Packet++/CMakeLists.txt b/Packet++/CMakeLists.txt index 7b6c2d0212..b8c5069dec 100644 --- a/Packet++/CMakeLists.txt +++ b/Packet++/CMakeLists.txt @@ -40,6 +40,7 @@ add_library( src/Packet.cpp src/PacketTrailerLayer.cpp src/PacketUtils.cpp + src/ParserConfig.cpp src/PayloadLayer.cpp src/PemCodec.cpp src/PPPoELayer.cpp @@ -118,6 +119,7 @@ set( header/Packet.h header/PacketTrailerLayer.h header/PacketUtils.h + header/ParserConfig.h header/PayloadLayer.h header/PemCodec.h header/PPPoELayer.h diff --git a/Packet++/header/ArpLayer.h b/Packet++/header/ArpLayer.h index 6402a5b753..f10a8214df 100644 --- a/Packet++/header/ArpLayer.h +++ b/Packet++/header/ArpLayer.h @@ -238,10 +238,6 @@ namespace pcpp // implement abstract methods - /// Does nothing for this layer (ArpLayer is always last) - void parseNextLayer() override - {} - /// @return The size of @ref arphdr size_t getHeaderLen() const override { @@ -280,6 +276,11 @@ namespace pcpp { return canReinterpretAs(data, dataLen); } + + protected: + /// Does nothing for this layer (ArpLayer is always last) + void doParseNextLayer(ParserConfiguration const& config) override + {} }; } // namespace pcpp diff --git a/Packet++/header/BgpLayer.h b/Packet++/header/BgpLayer.h index 00ced23bfe..0d9cdcd652 100644 --- a/Packet++/header/BgpLayer.h +++ b/Packet++/header/BgpLayer.h @@ -82,10 +82,6 @@ namespace pcpp /// @return The size of the BGP message size_t getHeaderLen() const override; - /// Multiple BGP messages can reside in a single packet, and the only layer that can come after a BGP message - /// is another BGP message. This method checks for remaining data and parses it as another BGP layer - void parseNextLayer() override; - std::string toString() const override; OsiModelLayer getOsiModelLayer() const override @@ -107,6 +103,10 @@ namespace pcpp : Layer(data, dataLen, prevLayer, packet, BGP) {} + /// Multiple BGP messages can reside in a single packet, and the only layer that can come after a BGP message + /// is another BGP message. This method checks for remaining data and parses it as another BGP layer + void doParseNextLayer(ParserConfiguration const& config) override; + bgp_common_header* getBasicHeader() const { return reinterpret_cast(m_Data); diff --git a/Packet++/header/CiscoHdlcLayer.h b/Packet++/header/CiscoHdlcLayer.h index fe444bdf73..7229cc019d 100644 --- a/Packet++/header/CiscoHdlcLayer.h +++ b/Packet++/header/CiscoHdlcLayer.h @@ -72,9 +72,6 @@ namespace pcpp /// Calculate the Next Protocol when possible void computeCalculateFields() override; - /// Parses the next layer. Currently, supports IPv4 and IPv6 - void parseNextLayer() override; - std::string toString() const override; OsiModelLayer getOsiModelLayer() const override @@ -82,6 +79,10 @@ namespace pcpp return OsiModelDataLinkLayer; } + protected: + /// Parses the next layer. Currently, supports IPv4 and IPv6 + void doParseNextLayer(ParserConfiguration const& config) override; + private: #pragma pack(push, 1) struct cisco_hdlc_header diff --git a/Packet++/header/CotpLayer.h b/Packet++/header/CotpLayer.h index bbd2ec5f18..dc2baaf4a9 100644 --- a/Packet++/header/CotpLayer.h +++ b/Packet++/header/CotpLayer.h @@ -75,9 +75,6 @@ namespace pcpp void computeCalculateFields() override {} - /// Currently parses the rest of the packet as a S7COMM or generic payload (PayloadLayer) - void parseNextLayer() override; - /// A static method that takes a byte array and detects whether it is a COTP /// @param[in] data A byte array /// @param[in] dataSize The byte array size (in bytes) @@ -91,6 +88,10 @@ namespace pcpp return OsiModelTransportLayer; } + protected: + /// Currently parses the rest of the packet as a S7COMM or generic payload (PayloadLayer) + void doParseNextLayer(ParserConfiguration const& config) override; + private: cotphdr* getCotpHeader() const { diff --git a/Packet++/header/DhcpLayer.h b/Packet++/header/DhcpLayer.h index 34daea5b8c..614ce65f05 100644 --- a/Packet++/header/DhcpLayer.h +++ b/Packet++/header/DhcpLayer.h @@ -732,10 +732,6 @@ namespace pcpp // implement abstract methods - /// Does nothing for this layer (DhcpLayer is always last) - void parseNextLayer() override - {} - /// @return The size of @ref dhcp_header + size of options size_t getHeaderLen() const override { @@ -759,6 +755,11 @@ namespace pcpp return OsiModelApplicationLayer; } + protected: + /// Does nothing for this layer (DhcpLayer is always last) + void doParseNextLayer(ParserConfiguration const& config) override + {} + private: uint8_t* getOptionsBasePtr() const { diff --git a/Packet++/header/DhcpV6Layer.h b/Packet++/header/DhcpV6Layer.h index 019274d550..4e9fb1bc7d 100644 --- a/Packet++/header/DhcpV6Layer.h +++ b/Packet++/header/DhcpV6Layer.h @@ -348,10 +348,6 @@ namespace pcpp // implement abstract methods - /// Does nothing for this layer (DhcpV6Layer is always last) - void parseNextLayer() override - {} - /// @return The size of @ref dhcpv6_header + size of options size_t getHeaderLen() const override { @@ -369,6 +365,11 @@ namespace pcpp return OsiModelApplicationLayer; } + protected: + /// Does nothing for this layer (DhcpV6Layer is always last) + void doParseNextLayer(ParserConfiguration const& config) override + {} + private: uint8_t* getOptionsBasePtr() const { diff --git a/Packet++/header/DnsLayer.h b/Packet++/header/DnsLayer.h index c79f269e27..39b2edd6bb 100644 --- a/Packet++/header/DnsLayer.h +++ b/Packet++/header/DnsLayer.h @@ -349,10 +349,6 @@ namespace pcpp // implement abstract methods - /// Does nothing for this layer (DnsLayer is always last) - void parseNextLayer() override - {} - /// @return The size of the DNS data in the packet including he DNS header and size of all queries, answers, /// authorities and additional records size_t getHeaderLen() const override @@ -388,6 +384,10 @@ namespace pcpp DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, size_t offsetAdjustment); explicit DnsLayer(size_t offsetAdjustment); + /// Does nothing for this layer (DnsLayer is always last) + void doParseNextLayer(ParserConfiguration const& config) override + {} + private: IDnsResource* m_ResourceList; DnsQuery* m_FirstQuery; diff --git a/Packet++/header/DoIpLayer.h b/Packet++/header/DoIpLayer.h index 04248833f9..0c5b380a5b 100644 --- a/Packet++/header/DoIpLayer.h +++ b/Packet++/header/DoIpLayer.h @@ -620,9 +620,6 @@ namespace pcpp // implement abstract methods - /// parse UDS layer - void parseNextLayer() override; - /// @return The size of @ref doiphdr + attached fields length size_t getHeaderLen() const override { @@ -639,6 +636,10 @@ namespace pcpp return OsiModelApplicationLayer; } + protected: + /// parse UDS layer + void doParseNextLayer(ParserConfiguration const& config) override; + private: void setPayloadType(DoIpPayloadTypes payloadType); diff --git a/Packet++/header/EthDot3Layer.h b/Packet++/header/EthDot3Layer.h index c725cd86f4..f500bcef3e 100644 --- a/Packet++/header/EthDot3Layer.h +++ b/Packet++/header/EthDot3Layer.h @@ -93,9 +93,6 @@ namespace pcpp // implement abstract methods - /// Parses next layer - void parseNextLayer() override; - /// @return Size of ether_dot3_header size_t getHeaderLen() const override { @@ -118,6 +115,10 @@ namespace pcpp /// @param[in] dataLen The length of the byte stream /// @return True if the data is valid and can represent an IEEE 802.3 Eth packet static bool isDataValid(const uint8_t* data, size_t dataLen); + + protected: + /// Parses next layer + void doParseNextLayer(ParserConfiguration const& config) override; }; } // namespace pcpp diff --git a/Packet++/header/EthLayer.h b/Packet++/header/EthLayer.h index 750dbfe691..8890effc47 100644 --- a/Packet++/header/EthLayer.h +++ b/Packet++/header/EthLayer.h @@ -130,10 +130,6 @@ namespace pcpp // implement abstract methods - /// Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, - /// PPPoESessionLayer, PPPoEDiscoveryLayer, MplsLayer. Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return Size of ether_header size_t getHeaderLen() const override { @@ -155,6 +151,11 @@ namespace pcpp /// @param[in] dataLen The length of the byte stream /// @return True if the data is valid and can represent an Ethernet II packet static bool isDataValid(const uint8_t* data, size_t dataLen); + + protected: + /// Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, + /// PPPoESessionLayer, PPPoEDiscoveryLayer, MplsLayer. Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; }; } // namespace pcpp diff --git a/Packet++/header/FtpLayer.h b/Packet++/header/FtpLayer.h index c0be452282..259d657adb 100644 --- a/Packet++/header/FtpLayer.h +++ b/Packet++/header/FtpLayer.h @@ -35,10 +35,6 @@ namespace pcpp // overridden methods - /// FTP is the always last so does nothing for this layer - void parseNextLayer() override - {} - /// @return Get the size of the layer size_t getHeaderLen() const override { @@ -54,6 +50,11 @@ namespace pcpp { return OsiModelApplicationLayer; } + + protected: + /// FTP is the always last so does nothing for this layer + void doParseNextLayer(ParserConfiguration const& config) override + {} }; /// Class for representing the request messages of FTP Layer diff --git a/Packet++/header/GreLayer.h b/Packet++/header/GreLayer.h index 38c25d64a6..0137db25b7 100644 --- a/Packet++/header/GreLayer.h +++ b/Packet++/header/GreLayer.h @@ -125,11 +125,6 @@ namespace pcpp // implement abstract methods - /// Currently identifies the following next layers: - /// IPv4Layer, IPv6Layer, VlanLayer, MplsLayer, PPP_PPTPLayer, EthLayer, EthDot3Layer - /// Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return Size of GRE header (may change if optional fields are added or removed) size_t getHeaderLen() const override; @@ -156,6 +151,11 @@ namespace pcpp uint8_t* getFieldValue(GreField field, bool returnOffsetEvenIfFieldMissing) const; + /// Currently identifies the following next layers: + /// IPv4Layer, IPv6Layer, VlanLayer, MplsLayer, PPP_PPTPLayer, EthLayer, EthDot3Layer + /// Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; + void computeCalculateFieldsInner(); }; @@ -367,9 +367,6 @@ namespace pcpp // implement abstract methods - /// Currently identifies the following next layers: IPv4Layer, IPv6Layer. Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return The size of @ref ppp_pptp_header size_t getHeaderLen() const override { @@ -389,6 +386,10 @@ namespace pcpp { return OsiModelSesionLayer; } + + protected: + /// Currently identifies the following next layers: IPv4Layer, IPv6Layer. Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; }; bool PPP_PPTPLayer::isDataValid(const uint8_t* data, size_t dataLen) diff --git a/Packet++/header/GtpLayer.h b/Packet++/header/GtpLayer.h index 91428fb3df..1168ac90ed 100644 --- a/Packet++/header/GtpLayer.h +++ b/Packet++/header/GtpLayer.h @@ -390,9 +390,6 @@ namespace pcpp // implement abstract methods - /// Identifies the following next layers for GTP-U packets: IPv4Layer, IPv6Layer. Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return The size of the GTP header. For GTP-C packets the size is determined by the value of /// gtpv1_header#messageLength and for GTP-U the size only includes the GTP header itself (meaning /// the size of gtpv1_header plus the size of the optional fields such as sequence number, N-PDU @@ -409,6 +406,10 @@ namespace pcpp { return OsiModelTransportLayer; } + + protected: + /// Identifies the following next layers for GTP-U packets: IPv4Layer, IPv6Layer. Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; }; /// @class GtpV2MessageType @@ -1098,9 +1099,6 @@ namespace pcpp // implement abstract methods - /// Identifies if the next layer is GTPv2 piggyback. Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return The size of the GTPv2 header including its Information Elements (IE) size_t getHeaderLen() const override; @@ -1114,6 +1112,10 @@ namespace pcpp return OsiModelTransportLayer; } + protected: + /// Identifies if the next layer is GTPv2 piggyback. Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; + private: #pragma pack(push, 1) struct gtpv2_basic_header diff --git a/Packet++/header/IPSecLayer.h b/Packet++/header/IPSecLayer.h index b38cd3a54f..2fdef918b4 100644 --- a/Packet++/header/IPSecLayer.h +++ b/Packet++/header/IPSecLayer.h @@ -91,10 +91,6 @@ namespace pcpp return static_cast(4) * (getAHHeader()->payloadLen + 2); } - /// Currently identifies the following next layers: UdpLayer, TcpLayer, IPv4Layer, IPv6Layer and ESPLayer. - /// Otherwise sets PayloadLayer - void parseNextLayer() override; - /// Does nothing for this layer void computeCalculateFields() override {} @@ -106,6 +102,11 @@ namespace pcpp return OsiModelNetworkLayer; } + protected: + /// Currently identifies the following next layers: UdpLayer, TcpLayer, IPv4Layer, IPv6Layer and ESPLayer. + /// Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; + private: // this layer supports parsing only AuthenticationHeaderLayer() @@ -151,9 +152,6 @@ namespace pcpp return sizeof(ipsec_esp); } - /// The payload of an ESP layer is encrypted, hence the next layer is always a generic payload (PayloadLayer) - void parseNextLayer() override; - /// Does nothing for this layer void computeCalculateFields() override {} @@ -165,6 +163,10 @@ namespace pcpp return OsiModelTransportLayer; } + protected: + /// The payload of an ESP layer is encrypted, hence the next layer is always a generic payload (PayloadLayer) + void doParseNextLayer(ParserConfiguration const& config) override; + private: // this layer supports parsing only ESPLayer() diff --git a/Packet++/header/IPv4Layer.h b/Packet++/header/IPv4Layer.h index 20829b9f4e..0c34856ece 100644 --- a/Packet++/header/IPv4Layer.h +++ b/Packet++/header/IPv4Layer.h @@ -563,20 +563,6 @@ namespace pcpp // implement abstract methods - /// Currently identifies the following next layers: - /// - UdpLayer - /// - TcpLayer - /// - IcmpLayer - /// - IPv4Layer (IP-in-IP) - /// - IPv6Layer (IP-in-IP) - /// - GreLayer - /// - IgmpLayer - /// - AuthenticationHeaderLayer (IPSec) - /// - ESPLayer (IPSec) - /// - /// Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return Size of IPv4 header (including IPv4 options if exist) size_t getHeaderLen() const override { @@ -605,6 +591,21 @@ namespace pcpp /// @return True if the data is valid and can represent an IPv4 packet static inline bool isDataValid(const uint8_t* data, size_t dataLen); + protected: + /// Currently identifies the following next layers: + /// - UdpLayer + /// - TcpLayer + /// - IcmpLayer + /// - IPv4Layer (IP-in-IP) + /// - IPv6Layer (IP-in-IP) + /// - GreLayer + /// - IgmpLayer + /// - AuthenticationHeaderLayer (IPSec) + /// - ESPLayer (IPSec) + /// + /// Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; + private: int m_NumOfTrailingBytes; int m_TempHeaderExtension; diff --git a/Packet++/header/IPv6Layer.h b/Packet++/header/IPv6Layer.h index 87cffaafbe..3cabe7742d 100644 --- a/Packet++/header/IPv6Layer.h +++ b/Packet++/header/IPv6Layer.h @@ -158,18 +158,6 @@ namespace pcpp // implement abstract methods - /// Currently identifies the following next layers: - /// - UdpLayer - /// - TcpLayer - /// - IPv4Layer (IP-in-IP) - /// - IPv6Layer (IP-in-IP) - /// - GreLayer - /// - AuthenticationHeaderLayer (IPSec) - /// - ESPLayer (IPSec) - /// - /// Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return Size of @ref ip6_hdr size_t getHeaderLen() const override { @@ -190,6 +178,19 @@ namespace pcpp return OsiModelNetworkLayer; } + protected: + /// Currently identifies the following next layers: + /// - UdpLayer + /// - TcpLayer + /// - IPv4Layer (IP-in-IP) + /// - IPv6Layer (IP-in-IP) + /// - GreLayer + /// - AuthenticationHeaderLayer (IPSec) + /// - ESPLayer (IPSec) + /// + /// Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; + private: void initLayer(); void parseExtensions(); diff --git a/Packet++/header/IcmpLayer.h b/Packet++/header/IcmpLayer.h index 940aa8ce3d..4290834a80 100644 --- a/Packet++/header/IcmpLayer.h +++ b/Packet++/header/IcmpLayer.h @@ -593,11 +593,6 @@ namespace pcpp // implement abstract methods - /// ICMP messages of types: ICMP_DEST_UNREACHABLE, ICMP_SOURCE_QUENCH, ICMP_TIME_EXCEEDED, ICMP_REDIRECT, - /// ICMP_PARAM_PROBLEM have data that contains IPv4 header and some L4 header (TCP/UDP/ICMP). This method parses - /// these headers as separate layers on top of the ICMP layer - void parseNextLayer() override; - /// @return The ICMP header length. This length varies according to the ICMP message type. This length doesn't /// include IPv4 and L4 headers in case ICMP message type are: ICMP_DEST_UNREACHABLE, ICMP_SOURCE_QUENCH, /// ICMP_TIME_EXCEEDED, ICMP_REDIRECT, ICMP_PARAM_PROBLEM @@ -612,6 +607,12 @@ namespace pcpp { return OsiModelNetworkLayer; } + + protected: + /// ICMP messages of types: ICMP_DEST_UNREACHABLE, ICMP_SOURCE_QUENCH, ICMP_TIME_EXCEEDED, ICMP_REDIRECT, + /// ICMP_PARAM_PROBLEM have data that contains IPv4 header and some L4 header (TCP/UDP/ICMP). This method parses + /// these headers as separate layers on top of the ICMP layer + void doParseNextLayer(ParserConfiguration const& config) override; }; // implementation of inline methods diff --git a/Packet++/header/IcmpV6Layer.h b/Packet++/header/IcmpV6Layer.h index 4038b5e2f2..46f85c9ea3 100644 --- a/Packet++/header/IcmpV6Layer.h +++ b/Packet++/header/IcmpV6Layer.h @@ -165,10 +165,6 @@ namespace pcpp /// @return Get the checksum header field in host representation uint16_t getChecksum() const; - /// Does nothing for this layer. ICMPv6 is the last layer. - void parseNextLayer() override - {} - /// @return The size of the ICMPv6 message size_t getHeaderLen() const override { @@ -188,6 +184,10 @@ namespace pcpp protected: IcmpV6Layer() = default; + /// Does nothing for this layer. ICMPv6 is the last layer. + void doParseNextLayer(ParserConfiguration const& config) override + {} + private: void calculateChecksum(); icmpv6hdr* getIcmpv6Header() const diff --git a/Packet++/header/IgmpLayer.h b/Packet++/header/IgmpLayer.h index 45e00a391a..3d64fb7bb0 100644 --- a/Packet++/header/IgmpLayer.h +++ b/Packet++/header/IgmpLayer.h @@ -187,10 +187,6 @@ namespace pcpp // implement abstract methods - /// Does nothing for this layer (IGMP layer is always last) - void parseNextLayer() override - {} - /// @return Size of IGMP header = 8B size_t getHeaderLen() const override { @@ -203,6 +199,11 @@ namespace pcpp { return OsiModelNetworkLayer; } + + protected: + /// Does nothing for this layer (IGMP layer is always last) + void doParseNextLayer(ParserConfiguration const& config) override + {} }; /// @class IgmpV1Layer diff --git a/Packet++/header/LLCLayer.h b/Packet++/header/LLCLayer.h index ddaf7a99ad..c00ca98c78 100644 --- a/Packet++/header/LLCLayer.h +++ b/Packet++/header/LLCLayer.h @@ -52,9 +52,6 @@ namespace pcpp // overridden methods - /// Parses the next layer. Currently only STP supported as next layer - void parseNextLayer() override; - /// Does nothing for this layer void computeCalculateFields() override {} @@ -79,6 +76,10 @@ namespace pcpp /// @param[in] dataLen The length of the byte stream /// @return True if the data is valid and can represent an LLC packet static bool isDataValid(const uint8_t* data, size_t dataLen); + + protected: + /// Parses the next layer. Currently only STP supported as next layer + void doParseNextLayer(ParserConfiguration const& config) override; }; } // namespace pcpp diff --git a/Packet++/header/Layer.h b/Packet++/header/Layer.h index eeb7eaa238..d4cd949444 100644 --- a/Packet++/header/Layer.h +++ b/Packet++/header/Layer.h @@ -3,6 +3,7 @@ #include #include #include "ProtocolType.h" +#include "ParserConfig.h" #include #include #include @@ -142,7 +143,15 @@ namespace pcpp // abstract methods /// Each layer is responsible for parsing the next layer - virtual void parseNextLayer() = 0; + void parseNextLayer() + { + parseNextLayer(ParserConfiguration::getDefault()); + } + + void parseNextLayer(ParserConfiguration const& config) + { + doParseNextLayer(config); + } /// @return The header length in bytes virtual size_t getHeaderLen() const = 0; @@ -197,6 +206,8 @@ namespace pcpp return m_NextLayer != nullptr; } + virtual void doParseNextLayer(ParserConfiguration const& config) = 0; + /// Construct the next layer in the protocol stack. No validation is performed on the data. /// @tparam T The type of the layer to construct /// @tparam Args The types of the arguments to pass to the layer constructor diff --git a/Packet++/header/LdapLayer.h b/Packet++/header/LdapLayer.h index 4485d17c28..e7864ffa24 100644 --- a/Packet++/header/LdapLayer.h +++ b/Packet++/header/LdapLayer.h @@ -358,9 +358,6 @@ namespace pcpp // implement abstract methods - /// Tries to identify more LDAP messages in this packet if exist - void parseNextLayer() override; - /// @return The size of the LDAP message size_t getHeaderLen() const override { @@ -385,6 +382,10 @@ namespace pcpp LdapLayer() = default; void init(uint16_t messageId, LdapOperationType operationType, const std::vector& messageRecords, const std::vector& controls); + + /// Tries to identify more LDAP messages in this packet if exist + void doParseNextLayer(ParserConfiguration const& config) override; + virtual std::string getExtendedInfoString() const { return ""; diff --git a/Packet++/header/ModbusLayer.h b/Packet++/header/ModbusLayer.h index bf50c4f986..7b3a8c22a1 100644 --- a/Packet++/header/ModbusLayer.h +++ b/Packet++/header/ModbusLayer.h @@ -131,10 +131,6 @@ namespace pcpp // Overridden methods - /// Does nothing for this layer (ModbusLayer is always last) - void parseNextLayer() override - {} - /// @brief Get the length of the MODBUS header /// @return Length of the MODBUS header in bytes size_t getHeaderLen() const override @@ -156,6 +152,11 @@ namespace pcpp return OsiModelApplicationLayer; } + protected: + /// Does nothing for this layer (ModbusLayer is always last) + void doParseNextLayer(ParserConfiguration const& config) override + {} + private: /// @return A pointer to the MODBUS header modbus_header* getModbusHeader() const; diff --git a/Packet++/header/MplsLayer.h b/Packet++/header/MplsLayer.h index 12ea1cd817..dec9114c7d 100644 --- a/Packet++/header/MplsLayer.h +++ b/Packet++/header/MplsLayer.h @@ -87,9 +87,6 @@ namespace pcpp // implement abstract methods - /// Currently identifies the following next layers: IPv4Layer, IPv6Layer, MplsLayer. Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return Size of MPLS header (4 bytes) size_t getHeaderLen() const override { @@ -115,6 +112,10 @@ namespace pcpp { return canReinterpretAs(data, dataLen); } + + protected: + /// Currently identifies the following next layers: IPv4Layer, IPv6Layer, MplsLayer. Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; }; } // namespace pcpp diff --git a/Packet++/header/NflogLayer.h b/Packet++/header/NflogLayer.h index ed997e50b5..a03b949a2e 100644 --- a/Packet++/header/NflogLayer.h +++ b/Packet++/header/NflogLayer.h @@ -182,10 +182,6 @@ namespace pcpp // implement abstract methods - /// Currently identifies the following next layers: IPv4Layer, IPv6Layer using address family - /// Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return Size of nflog_header size_t getHeaderLen() const override; @@ -205,6 +201,11 @@ namespace pcpp /// @return True if the data is valid and can represent an NFLOG packet static bool isDataValid(const uint8_t* data, size_t dataLen); + protected: + /// Currently identifies the following next layers: IPv4Layer, IPv6Layer using address family + /// Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; + private: uint8_t* getTlvsBasePtr() const { diff --git a/Packet++/header/NtpLayer.h b/Packet++/header/NtpLayer.h index 7dec0ae463..e5efbaf2b7 100644 --- a/Packet++/header/NtpLayer.h +++ b/Packet++/header/NtpLayer.h @@ -535,10 +535,6 @@ namespace pcpp // overridden methods - /// Parses the next layer. NTP is the always last so does nothing for this layer - void parseNextLayer() override - {} - /// @return Get the size of the layer (Including the extension and authentication fields if exists) size_t getHeaderLen() const override { @@ -557,5 +553,10 @@ namespace pcpp /// @return Returns the protocol info as readable string std::string toString() const override; + + protected: + /// Parses the next layer. NTP is the always last so does nothing for this layer + void doParseNextLayer(ParserConfiguration const& config) override + {} }; } // namespace pcpp diff --git a/Packet++/header/NullLoopbackLayer.h b/Packet++/header/NullLoopbackLayer.h index 0903781771..c36ad9a177 100644 --- a/Packet++/header/NullLoopbackLayer.h +++ b/Packet++/header/NullLoopbackLayer.h @@ -55,13 +55,6 @@ namespace pcpp // implement abstract methods - /// Identifies the next layers by family: - /// - for ::PCPP_BSD_AF_INET the next layer is IPv4Layer - /// - for ::PCPP_BSD_AF_INET6_BSD, ::PCPP_BSD_AF_INET6_FREEBSD, ::PCPP_BSD_AF_INET6_DARWIN the next layer is - /// IPv6Layer - /// - for other values the next layer in PayloadLayer (unknown protocol) - void parseNextLayer() override; - /// @return Size of Null/Loopback header = 4B size_t getHeaderLen() const override { @@ -78,5 +71,13 @@ namespace pcpp { return OsiModelDataLinkLayer; } + + protected: + /// Identifies the next layers by family: + /// - for ::PCPP_BSD_AF_INET the next layer is IPv4Layer + /// - for ::PCPP_BSD_AF_INET6_BSD, ::PCPP_BSD_AF_INET6_FREEBSD, ::PCPP_BSD_AF_INET6_DARWIN the next layer is + /// IPv6Layer + /// - for other values the next layer in PayloadLayer (unknown protocol) + void doParseNextLayer(ParserConfiguration const& config) override; }; } // namespace pcpp diff --git a/Packet++/header/PPPoELayer.h b/Packet++/header/PPPoELayer.h index aba14b0c8a..e4fffbedc3 100644 --- a/Packet++/header/PPPoELayer.h +++ b/Packet++/header/PPPoELayer.h @@ -148,9 +148,6 @@ namespace pcpp // abstract methods implementation - /// Currently identifies the following next layers: IPv4Layer, IPv6Layer. Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return Size of @ref pppoe_header size_t getHeaderLen() const override { @@ -158,6 +155,10 @@ namespace pcpp } std::string toString() const override; + + protected: + /// Currently identifies the following next layers: IPv4Layer, IPv6Layer. Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; }; /// @class PPPoEDiscoveryLayer @@ -349,10 +350,6 @@ namespace pcpp // abstract methods implementation - /// Does nothing for this layer (PPPoE discovery is always the last layer) - void parseNextLayer() override - {} - /// @return The header length which is size of strcut pppoe_header plus the total size of tags size_t getHeaderLen() const override; @@ -361,6 +358,11 @@ namespace pcpp return "PPP-over-Ethernet Discovery (" + codeToString((PPPoELayer::PPPoECode)getPPPoEHeader()->code) + ")"; } + protected: + /// Does nothing for this layer (PPPoE discovery is always the last layer) + void doParseNextLayer(ParserConfiguration const& config) override + {} + private: TLVRecordReader m_TagReader; diff --git a/Packet++/header/PacketTrailerLayer.h b/Packet++/header/PacketTrailerLayer.h index fdcb8e6487..c69d8902b8 100644 --- a/Packet++/header/PacketTrailerLayer.h +++ b/Packet++/header/PacketTrailerLayer.h @@ -61,10 +61,6 @@ namespace pcpp // implement abstract methods - /// Does nothing for this layer (PacketTrailerLayer is always last) - void parseNextLayer() override - {} - /// @return trailer data length in bytes size_t getHeaderLen() const override { @@ -81,6 +77,11 @@ namespace pcpp { return OsiModelDataLinkLayer; } + + protected: + /// Does nothing for this layer (PacketTrailerLayer is always last) + void doParseNextLayer(ParserConfiguration const& config) override + {} }; } // namespace pcpp diff --git a/Packet++/header/ParserConfig.h b/Packet++/header/ParserConfig.h new file mode 100644 index 0000000000..89edf05c6c --- /dev/null +++ b/Packet++/header/ParserConfig.h @@ -0,0 +1,328 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "Logger.h" +#include "ProtocolType.h" + +namespace pcpp +{ + /// @brief Represents a pair of ports, typically used for network protocol matching. + class PortPair + { + public: + /// @brief Represents a value that indicates any port can be matched. + static constexpr struct AnyPortWildcard + { + } AnyPort = {}; + + constexpr PortPair() = default; + constexpr PortPair(AnyPortWildcard, AnyPortWildcard) : PortPair() + {} + + constexpr PortPair(uint16_t portSrc, uint16_t portDst) + : m_PortSrc(portSrc), m_PortDst(portDst), m_PortSrcSet(true), m_PortDstSet(true) + {} + + constexpr PortPair(uint16_t portSrc, AnyPortWildcard) + : m_PortSrc(portSrc), m_PortDst(0), m_PortSrcSet(true), m_PortDstSet(false) + {} + + constexpr PortPair(AnyPortWildcard, uint16_t portDst) + : m_PortSrc(0), m_PortDst(portDst), m_PortSrcSet(false), m_PortDstSet(true) + {} + + /// @brief Constructs a PortPair with the specified source port and destination port set to AnyPort. + /// @param portSrc Source port number. + /// @return A PortPair with the specified source port and destination port set to AnyPort. + constexpr static PortPair fromSrc(uint16_t portSrc) + { + return { portSrc, AnyPort }; + } + + /// @brief Constructs a PortPair with the specified destination port and source port set to AnyPort. + /// @param portDst Destination port number. + /// @return A PortPair with the specified destination port and source port set to AnyPort. + constexpr static PortPair fromDst(uint16_t portDst) + { + return { AnyPort, portDst }; + } + + /// @brief Returns a PortPair with the source port replaced by AnyPort wildcard. + /// @return A new PortPair with the source port set to AnyPort. + constexpr PortPair withAnySrc() const + { + if (m_PortDstSet) + { + return fromDst(m_PortDst); + } + + return PortPair{ AnyPort, AnyPort }; + } + + /// @brief Returns a PortPair with the destination port replaced by AnyPort wildcard. + /// @return A new PortPair with the destination port set to AnyPort. + constexpr PortPair withAnyDst() const + { + if (m_PortSrcSet) + { + return fromSrc(m_PortSrc); + } + + return PortPair{ AnyPort, AnyPort }; + } + + /// @brief Returns a PortPair with the source and destination ports reversed. + /// @return A new PortPair with the source and destination ports swapped. + constexpr PortPair withSwappedPorts() const noexcept + { + PortPair reversed; + reversed.m_PortSrc = m_PortDst; + reversed.m_PortSrcSet = m_PortDstSet; + reversed.m_PortDst = m_PortSrc; + reversed.m_PortDstSet = m_PortSrcSet; + return reversed; + } + + constexpr bool isSrcPortSet() const noexcept + { + return m_PortSrcSet; + } + + constexpr bool isDstPortSet() const noexcept + { + return m_PortDstSet; + } + + constexpr bool hasWildcards() const noexcept + { + return !(isSrcPortSet() && isDstPortSet()); + } + + constexpr uint16_t portSrc() const noexcept + { + return m_PortSrc; + } + + constexpr uint16_t portDst() const noexcept + { + return m_PortDst; + } + + constexpr void setSrcPort(uint16_t portSrc) noexcept + { + m_PortSrc = portSrc; + m_PortSrcSet = true; + } + + constexpr void setSrcPort(AnyPortWildcard) noexcept + { + m_PortSrc = 0; + m_PortSrcSet = false; + } + + constexpr void setDstPort(uint16_t portDst) noexcept + { + m_PortDst = portDst; + m_PortDstSet = true; + } + + constexpr void setDstPort(AnyPortWildcard) noexcept + { + m_PortDst = 0; + m_PortDstSet = false; + } + + constexpr bool operator==(const PortPair& other) const noexcept + { + return comparePort(m_PortSrc, other.m_PortSrc, m_PortSrcSet, other.m_PortSrcSet) && + comparePort(m_PortDst, other.m_PortDst, m_PortDstSet, other.m_PortDstSet); + } + + constexpr bool operator!=(const PortPair& other) const noexcept + { + return !(*this == other); + } + + friend std::ostream& operator<<(std::ostream& os, PortPair const pair) + { + os << "PortPair(src: "; + if (pair.m_PortSrcSet) + { + os << pair.m_PortSrc; + } + else + { + os << "AnyPort"; + } + + os << ", dst: "; + if (pair.m_PortDstSet) + { + os << pair.m_PortDst; + } + else + { + os << "AnyPort"; + } + os << ')'; + return os; + } + + private: + constexpr static bool comparePort(uint16_t port1, uint16_t port2, bool isSet1, bool isSet2) noexcept + { + // If both ports are set, compare them directly + // If one of the ports is not set, return false. + // If both ports are not set, return true. + + if (isSet1 && isSet2) + { + return port1 == port2; + } + return !isSet1 && !isSet2; // Both ports are wildcard + } + + uint16_t m_PortSrc = 0; ///< Source port number + uint16_t m_PortDst = 0; ///< Destination port number + bool m_PortSrcSet = false; ///< Indicates if the src port is set, on false consider the port as wildcard + bool m_PortDstSet = false; ///< Indicates if the dst port is set, on false consider the port as wildcard + }; + + /// @brief A class that maps port pairs to protocol types. + class PortMapper + { + public: + /// @brief Create an empty PortMapper. + PortMapper() = default; + + /// @brief Add a port mapping to the port mapper. + /// @param port The port number to map. + /// @param protocolFamily The ProtocolTypeFamily to associate with the port. + /// @param symmetrical If true, the mapping is considered symmetrical (both src and dst ports are the + /// interchangeable). + void addPortMapping(PortPair port, ProtocolTypeFamily protocolFamily, bool symmetrical = false); + + /// @brief Remove a port mapping from the port mapper. + /// @param port The port number to remove from the mapping. + /// @param symmetrical If true, the mapping is considered symmetrical (both src and dst ports are the + /// interchangeable). + void removePortMapping(PortPair port, bool symmetrical = false); + + /// @brief Get the protocol type associated with a specific port. + /// @param port The port number to look up. + /// @return The ProtocolTypeFamily associated with the port, or UnknownProtocol if not found. + ProtocolTypeFamily getProtocolByPortPair(PortPair port) const; + + /// @brief Get the protocol mappings that correspond to a specific port pair. + /// + /// The method returns an array of ProtocolType values associated with the port pair. + /// + /// The elements in the array represent the following mappings: + /// Src Port | Dst Port | Match Type + /// - Index 0: Original | Original | Full Match + /// - Index 1: Original | Any Port | Src Port Match + /// - Index 2: Any Port | Original | Dst Port Match + /// + /// If a port pair is not found, the corresponding index in the array will contain UnknownProtocol. + /// If a port pair is not mapped to any protocol, the array will contain UnknownProtocol in all indices. + /// + /// @param port The port pair to look up. + /// @return An array of ProtocolTypeFamily values representing the protocols associated with the port pair. + std::array getMatchMatrix(PortPair port) const; + + /// @brief Get the protocol mappings that correspond to a specific port pair. + /// + /// See `getMatchMatrix(PortPair port)` for details. + /// + /// @param portSrc The source port number. + /// @param portDst The destination port number. + /// @return An array of ProtocolTypeFamily values representing the protocols associated with the port pair. + std::array getMatchMatrix(uint16_t portSrc, uint16_t portDst) const + { + return getMatchMatrix({ portSrc, portDst }); + } + + /// @brief Check if a port matches a specific protocol type. + /// @param port The port number to check. + /// @param protocolFamily The ProtocolTypeFamily to match against. + /// @return True if the port matches the protocol type, false otherwise. + bool matchesPortAndProtocol(PortPair port, ProtocolTypeFamily protocolFamily) const + { + return getProtocolByPortPair(port) == protocolFamily; + } + + /// @brief Creates a default PortMapper with common port mappings. + /// @return A PortMapper instance with default port mappings. + static PortMapper makeDefaultPortMapper(); + + private: + struct PackedPortPairHasher + { + std::size_t operator()(const PortPair& portPair) const noexcept + { + return static_cast(encodePair(portPair)); + // return static_cast(splitMix64(encodePair(portPair))); + } + + constexpr uint64_t encodePair(const PortPair& portPair) const noexcept + { + // Encodes the port pair as a uint64_t + // 30 bits unused, 2 bits wildcard flags, 16 bits for src port, 16 bits for dst port, + uint64_t srcPort = portPair.portSrc(); + uint64_t dstPort = static_cast(portPair.portDst()) << 16; + uint64_t srcPortSet = static_cast(portPair.isSrcPortSet() ? 1 : 0) << 32; + uint64_t dstPortSet = static_cast(portPair.isDstPortSet() ? 1 : 0) << 33; + + return srcPort | dstPort | srcPortSet | dstPortSet; + } + + constexpr uint64_t splitMix64(uint64_t key) const noexcept + { + uint64_t z = key + 0x9E3779B97F4A7C15ULL; + z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL; + z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL; + return z ^ (z >> 31); + } + }; + + // todo: profile lookup performance. + std::unordered_map m_PortToProtocolMap; + }; + + /// @brief A parser configuration that can be used to configure the behavior of the packet parser. + struct ParserConfiguration + { + ParserConfiguration() = default; + + PortMapper portMapper; + + /// @brief Creates a new instance of ParserConfiguration with default settings. + /// + /// Prefer using `getDefault()` to obtain the default configuration if a new instance is not required. + /// + /// @return A ParserConfiguration instance with default port mappings. + static ParserConfiguration makeDefaultConfiguration(); + + /// @brief Get the default parser configuration. + /// + /// The returned reference can be used to configure the parser globally. + /// + /// @return A reference to the default ParserConfiguration instance. + static inline ParserConfiguration& getDefault() + { + static ParserConfiguration defaultConfig = makeDefaultConfiguration(); + return defaultConfig; + } + + /// @brief Reset the default parser configuration to its initial state. + static inline void resetDefault() + { + getDefault() = makeDefaultConfiguration(); + } + }; +} // namespace pcpp diff --git a/Packet++/header/PayloadLayer.h b/Packet++/header/PayloadLayer.h index 2c760d1178..9988a548c1 100644 --- a/Packet++/header/PayloadLayer.h +++ b/Packet++/header/PayloadLayer.h @@ -53,10 +53,6 @@ namespace pcpp // implement abstract methods - /// Does nothing for this layer (PayloadLayer is always last) - void parseNextLayer() override - {} - /// @return Payload data length in bytes size_t getHeaderLen() const override { @@ -89,5 +85,10 @@ namespace pcpp // PayloadLayer is special as it can be empty. So, it's valid if data is nullptr and dataLen is 0. return (data == nullptr) != (dataLen != 0); // XOR }; + + protected: + /// Does nothing for this layer (PayloadLayer is always last) + void doParseNextLayer(ParserConfiguration const& config) override + {} }; } // namespace pcpp diff --git a/Packet++/header/RadiusLayer.h b/Packet++/header/RadiusLayer.h index f65fb66b66..d00facbb41 100644 --- a/Packet++/header/RadiusLayer.h +++ b/Packet++/header/RadiusLayer.h @@ -253,10 +253,6 @@ namespace pcpp /// @return The size written in radius_header#length size_t getHeaderLen() const override; - /// Does nothing for this layer, RADIUS is always last - void parseNextLayer() override - {} - /// Calculate and store the value of radius_header#length according to the layer size void computeCalculateFields() override; @@ -266,6 +262,11 @@ namespace pcpp { return OsiModelApplicationLayer; } + + protected: + /// Does nothing for this layer, RADIUS is always last + void doParseNextLayer(ParserConfiguration const& config) override + {} }; // implementation of inline methods diff --git a/Packet++/header/S7CommLayer.h b/Packet++/header/S7CommLayer.h index 6a65e7913b..db7a983e5b 100644 --- a/Packet++/header/S7CommLayer.h +++ b/Packet++/header/S7CommLayer.h @@ -151,10 +151,6 @@ namespace pcpp void computeCalculateFields() override {} - /// Does nothing for this layer (S7CommLayer is always last) - void parseNextLayer() override - {} - /// A static method that takes a byte array and detects whether it is a S7COMM /// @param[in] data A byte array /// @param[in] dataSize The byte array size (in bytes) @@ -168,6 +164,11 @@ namespace pcpp return OsiModelApplicationLayer; } + protected: + /// Does nothing for this layer (S7CommLayer is always last) + void doParseNextLayer(ParserConfiguration const& config) override + {} + private: s7commhdr* getS7commHeader() const { diff --git a/Packet++/header/SSHLayer.h b/Packet++/header/SSHLayer.h index ef07f14d5f..3dface7081 100644 --- a/Packet++/header/SSHLayer.h +++ b/Packet++/header/SSHLayer.h @@ -96,10 +96,6 @@ namespace pcpp // implement abstract methods - /// Several SSH records can reside in a single packets. This method examins the remaining data and creates - /// additional SSH records if applicable - void parseNextLayer() override; - /// Does nothing for this layer void computeCalculateFields() override {} @@ -115,6 +111,10 @@ namespace pcpp : Layer(data, dataLen, prevLayer, packet, SSH) {} + /// Several SSH records can reside in a single packets. This method examins the remaining data and creates + /// additional SSH records if applicable + void doParseNextLayer(ParserConfiguration const& config) override; + private: // this layer supports only parsing SSHLayer(); diff --git a/Packet++/header/SSLLayer.h b/Packet++/header/SSLLayer.h index 5c7a5fc010..4789d128f4 100644 --- a/Packet++/header/SSLLayer.h +++ b/Packet++/header/SSLLayer.h @@ -239,10 +239,6 @@ namespace pcpp /// @return The record size as extracted from the record data (in ssl_tls_record_layer#length) size_t getHeaderLen() const override; - /// Several SSL/TLS records can reside in a single packets. So this method checks the remaining data and if it's - /// identified as SSL/TLS it creates another SSL/TLS record layer as the next layer - void parseNextLayer() override; - OsiModelLayer getOsiModelLayer() const override { return OsiModelPresentationLayer; @@ -253,6 +249,9 @@ namespace pcpp : Layer(data, dataLen, prevLayer, packet, SSL) {} + /// Several SSL/TLS records can reside in a single packets. So this method checks the remaining data and if it's + /// identified as SSL/TLS it creates another SSL/TLS record layer as the next layer + void doParseNextLayer(ParserConfiguration const& config) override; }; // class SSLLayer // The graph below will break the code formatting, so it's disabled. diff --git a/Packet++/header/SipLayer.h b/Packet++/header/SipLayer.h index bd25e4cd83..a429c9c1bb 100644 --- a/Packet++/header/SipLayer.h +++ b/Packet++/header/SipLayer.h @@ -98,11 +98,6 @@ namespace pcpp return OsiModelSesionLayer; } - /// Currently identifies only SDP if content-length field exists and set to a value greater than zero. - /// If content-length field doesn't exist or set to zero and still there is data after this layer, a - /// PayloadLayer will be created - void parseNextLayer() override; - /// Set the content-length only if a content-length field already exists and if its current value is different /// than the total length of the next layer(s) void computeCalculateFields() override; @@ -128,6 +123,11 @@ namespace pcpp return *this; } + /// Currently identifies only SDP if content-length field exists and set to a value greater than zero. + /// If content-length field doesn't exist or set to zero and still there is data after this layer, a + /// PayloadLayer will be created + void doParseNextLayer(ParserConfiguration const& config) override; + // implementation of abstract methods char getHeaderFieldNameValueSeparator() const override { diff --git a/Packet++/header/Sll2Layer.h b/Packet++/header/Sll2Layer.h index 83a55253c2..f3243a0b45 100644 --- a/Packet++/header/Sll2Layer.h +++ b/Packet++/header/Sll2Layer.h @@ -128,10 +128,6 @@ namespace pcpp // implement abstract methods - /// Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, - /// PPPoESessionLayer, PPPoEDiscoveryLayer, MplsLayer. Otherwise sets PayloadLayer - void parseNextLayer() override; - /// Calculate the next protocol type for known protocols: IPv4, IPv6, ARP, VLAN void computeCalculateFields() override; @@ -147,6 +143,11 @@ namespace pcpp { return OsiModelDataLinkLayer; } + + protected: + /// Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, + /// PPPoESessionLayer, PPPoEDiscoveryLayer, MplsLayer. Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; }; } // namespace pcpp diff --git a/Packet++/header/SllLayer.h b/Packet++/header/SllLayer.h index a97b83e983..b795c21172 100644 --- a/Packet++/header/SllLayer.h +++ b/Packet++/header/SllLayer.h @@ -69,10 +69,6 @@ namespace pcpp /// @return True if address was set successfully, false if MAC address isn't valid or if set failed bool setMacAddressAsLinkLayer(const MacAddress& macAddr); - /// Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, - /// PPPoESessionLayer, PPPoEDiscoveryLayer, MplsLayer. Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return Size of sll_header size_t getHeaderLen() const override { @@ -88,6 +84,11 @@ namespace pcpp { return OsiModelDataLinkLayer; } + + protected: + /// Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, + /// PPPoESessionLayer, PPPoEDiscoveryLayer, MplsLayer. Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; }; } // namespace pcpp diff --git a/Packet++/header/SmtpLayer.h b/Packet++/header/SmtpLayer.h index 871a597233..5886950753 100644 --- a/Packet++/header/SmtpLayer.h +++ b/Packet++/header/SmtpLayer.h @@ -31,10 +31,6 @@ namespace pcpp // overridden methods - /// SMTP is the always last so does nothing for this layer - void parseNextLayer() override - {} - /// @return Get the size of the layer size_t getHeaderLen() const override { @@ -50,6 +46,11 @@ namespace pcpp { return OsiModelApplicationLayer; } + + protected: + /// SMTP is the always last so does nothing for this layer + void doParseNextLayer(ParserConfiguration const& config) override + {} }; /// Class for representing the request messages of SMTP Layer diff --git a/Packet++/header/SomeIpLayer.h b/Packet++/header/SomeIpLayer.h index 23a2204e54..4eb263b1bc 100644 --- a/Packet++/header/SomeIpLayer.h +++ b/Packet++/header/SomeIpLayer.h @@ -257,9 +257,6 @@ namespace pcpp virtual void computeCalculateFields() override {} - /// Identifies the following next layers: SomeIpLayer, SomeIpTpLayer, SomeIpSdLayer. Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return The string representation of the SOME/IP layer virtual std::string toString() const override; @@ -273,6 +270,9 @@ namespace pcpp SomeIpLayer() {} + /// Identifies the following next layers: SomeIpLayer, SomeIpTpLayer, SomeIpSdLayer. Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; + private: static const uint8_t SOMEIP_PROTOCOL_VERSION = 1; virtual size_t getSomeIpHeaderLen() const diff --git a/Packet++/header/StpLayer.h b/Packet++/header/StpLayer.h index 26c90312f4..9e5dd08370 100644 --- a/Packet++/header/StpLayer.h +++ b/Packet++/header/StpLayer.h @@ -261,9 +261,6 @@ namespace pcpp return sizeof(stp_tcn_bpdu); } - /// Parses next layer - void parseNextLayer() override; - /// @return Returns the protocol info as readable string std::string toString() const override { @@ -279,6 +276,10 @@ namespace pcpp { return canReinterpretAs(data, dataLen); } + + protected: + /// Parses next layer + void doParseNextLayer(ParserConfiguration const& config) override; }; /// @class StpConfigurationBPDULayer @@ -450,9 +451,6 @@ namespace pcpp return sizeof(stp_conf_bpdu); } - /// Parses next layer - void parseNextLayer() override; - /// @return Returns the protocol info as readable string std::string toString() const override { @@ -467,6 +465,10 @@ namespace pcpp { return canReinterpretAs(data, dataLen); } + + protected: + /// Parses next layer + void doParseNextLayer(ParserConfiguration const& config) override; }; /// @class RapidStpLayer @@ -520,9 +522,6 @@ namespace pcpp return sizeof(rstp_conf_bpdu); } - /// Parses next layer - void parseNextLayer() override; - /// @return Returns the protocol info as readable string std::string toString() const override { @@ -537,6 +536,10 @@ namespace pcpp { return canReinterpretAs(data, dataLen); } + + protected: + /// Parses next layer + void doParseNextLayer(ParserConfiguration const& config) override; }; /// @class MultipleStpLayer @@ -687,10 +690,6 @@ namespace pcpp // overridden methods - /// Parses next layer - void parseNextLayer() override - {} - /// @return Returns the protocol info as readable string std::string toString() const override { @@ -705,5 +704,10 @@ namespace pcpp { return canReinterpretAs(data, dataLen); } + + protected: + /// Parses next layer + void doParseNextLayer(ParserConfiguration const& config) override + {} }; } // namespace pcpp diff --git a/Packet++/header/TcpLayer.h b/Packet++/header/TcpLayer.h index 9f8e4b1d2a..7e4cb8f983 100644 --- a/Packet++/header/TcpLayer.h +++ b/Packet++/header/TcpLayer.h @@ -557,10 +557,6 @@ namespace pcpp // implement abstract methods - /// Currently identifies the following next layers: HttpRequestLayer, HttpResponseLayer. Otherwise sets - /// PayloadLayer - void parseNextLayer() override; - /// @return Size of @ref tcphdr + all TCP options size_t getHeaderLen() const override { @@ -577,6 +573,11 @@ namespace pcpp return OsiModelTransportLayer; } + protected: + /// Currently identifies the following next layers: HttpRequestLayer, HttpResponseLayer. Otherwise sets + /// PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; + private: TLVRecordReader m_OptionReader; int m_NumOfTrailingBytes; diff --git a/Packet++/header/TelnetLayer.h b/Packet++/header/TelnetLayer.h index 8f3baa35f6..5a0ffeb58b 100644 --- a/Packet++/header/TelnetLayer.h +++ b/Packet++/header/TelnetLayer.h @@ -289,10 +289,6 @@ namespace pcpp // overridden methods - /// Parses the next layer. Telnet is the always last so does nothing for this layer - void parseNextLayer() override - {} - /// @return Get the size of the layer size_t getHeaderLen() const override { @@ -311,6 +307,11 @@ namespace pcpp /// @return Returns the protocol info as readable string std::string toString() const override; + + protected: + /// Parses the next layer. Telnet is the always last so does nothing for this layer + void doParseNextLayer(ParserConfiguration const& config) override + {} }; } // namespace pcpp diff --git a/Packet++/header/TextBasedProtocol.h b/Packet++/header/TextBasedProtocol.h index 67e109ca7f..dc3e27419b 100644 --- a/Packet++/header/TextBasedProtocol.h +++ b/Packet++/header/TextBasedProtocol.h @@ -200,9 +200,6 @@ namespace pcpp // implement Layer's abstract methods - /// Currently set only PayloadLayer for the rest of the data - void parseNextLayer() override; - /// @return The message length size_t getHeaderLen() const override; @@ -221,6 +218,9 @@ namespace pcpp void copyDataFrom(const TextBasedProtocolMessage& other); + /// Currently set only PayloadLayer for the rest of the data + void doParseNextLayer(ParserConfiguration const& config) override; + void parseFields(); void shiftFieldsOffset(HeaderField* fromField, int numOfBytesToShift); diff --git a/Packet++/header/TpktLayer.h b/Packet++/header/TpktLayer.h index 073942ca54..8d0ab8b699 100644 --- a/Packet++/header/TpktLayer.h +++ b/Packet++/header/TpktLayer.h @@ -72,9 +72,6 @@ namespace pcpp void computeCalculateFields() override {} - /// Currently parses the rest of the packet as a COTP protocol or generic payload (PayloadLayer) - void parseNextLayer() override; - /// A static method that checks whether a source or dest port match those associated with the TPKT protocol /// @param[in] portSrc Source port number to check /// @param[in] portDst Dest port number to check @@ -100,6 +97,10 @@ namespace pcpp return OsiModelTransportLayer; } + protected: + /// Currently parses the rest of the packet as a COTP protocol or generic payload (PayloadLayer) + void doParseNextLayer(ParserConfiguration const& config) override; + private: /// Get a pointer to the TPKT header. Data can be retrieved through the /// other methods of this layer. Notice the return value points directly to the data, so every change will diff --git a/Packet++/header/UdpLayer.h b/Packet++/header/UdpLayer.h index c78bc2ded9..3f2d7613fe 100644 --- a/Packet++/header/UdpLayer.h +++ b/Packet++/header/UdpLayer.h @@ -72,10 +72,6 @@ namespace pcpp // implement abstract methods - /// Currently identifies the following next layers: DnsLayer, DhcpLayer, VxlanLayer, SipRequestLayer, - /// SipResponseLayer, RadiusLayer. Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return Size of @ref udphdr size_t getHeaderLen() const override { @@ -91,6 +87,11 @@ namespace pcpp { return OsiModelTransportLayer; } + + protected: + /// Currently identifies the following next layers: DnsLayer, DhcpLayer, VxlanLayer, SipRequestLayer, + /// SipResponseLayer, RadiusLayer. Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; }; bool UdpLayer::isDataValid(const uint8_t* data, size_t dataLen) diff --git a/Packet++/header/VlanLayer.h b/Packet++/header/VlanLayer.h index 238d36bd1d..482f64932e 100644 --- a/Packet++/header/VlanLayer.h +++ b/Packet++/header/VlanLayer.h @@ -92,10 +92,6 @@ namespace pcpp // implement abstract methods - /// Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, MplsLayer. - /// Otherwise sets PayloadLayer - void parseNextLayer() override; - /// @return Size of vlan_header size_t getHeaderLen() const override { @@ -120,5 +116,10 @@ namespace pcpp { return canReinterpretAs(data, dataLen); } + + protected: + /// Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, MplsLayer. + /// Otherwise sets PayloadLayer + void doParseNextLayer(ParserConfiguration const& config) override; }; } // namespace pcpp diff --git a/Packet++/header/VrrpLayer.h b/Packet++/header/VrrpLayer.h index eb6f888d88..a6a951183b 100644 --- a/Packet++/header/VrrpLayer.h +++ b/Packet++/header/VrrpLayer.h @@ -243,10 +243,6 @@ namespace pcpp // implement abstract methods - /// Does nothing for this layer (VRRP layer is always last) - void parseNextLayer() override - {} - /// Calculate the VRRP checksum void computeCalculateFields() override; @@ -263,6 +259,11 @@ namespace pcpp { return OsiModelNetworkLayer; } + + protected: + /// Does nothing for this layer (VRRP layer is always last) + void doParseNextLayer(ParserConfiguration const& config) override + {} }; /// @class VrrpV2Layer diff --git a/Packet++/header/VxlanLayer.h b/Packet++/header/VxlanLayer.h index 0924dc5b3e..e40debed98 100644 --- a/Packet++/header/VxlanLayer.h +++ b/Packet++/header/VxlanLayer.h @@ -114,9 +114,6 @@ namespace pcpp // implement abstract methods - /// Next layer for VXLAN is always Ethernet - void parseNextLayer() override; - /// @return Size of vxlan_header size_t getHeaderLen() const override { @@ -133,5 +130,9 @@ namespace pcpp { return OsiModelDataLinkLayer; } + + protected: + /// Next layer for VXLAN is always Ethernet + void doParseNextLayer(ParserConfiguration const& config) override; }; } // namespace pcpp diff --git a/Packet++/header/WakeOnLanLayer.h b/Packet++/header/WakeOnLanLayer.h index 3e98acc46a..78d14f22e5 100644 --- a/Packet++/header/WakeOnLanLayer.h +++ b/Packet++/header/WakeOnLanLayer.h @@ -115,10 +115,6 @@ namespace pcpp // overridden methods - /// Parses the next layer. Wake on LAN is the always last so does nothing for this layer - void parseNextLayer() override - {} - /// @return Get the size of the layer size_t getHeaderLen() const override { @@ -137,5 +133,10 @@ namespace pcpp /// @return Returns the protocol info as readable string std::string toString() const override; + + protected: + /// Parses the next layer. Wake on LAN is the always last so does nothing for this layer + void doParseNextLayer(ParserConfiguration const& config) override + {} }; } // namespace pcpp diff --git a/Packet++/header/WireGuardLayer.h b/Packet++/header/WireGuardLayer.h index 0d59287e93..0e5d32f286 100644 --- a/Packet++/header/WireGuardLayer.h +++ b/Packet++/header/WireGuardLayer.h @@ -95,10 +95,6 @@ namespace pcpp /// @param reserved The reserved field to set as a An array containing the 3-byte. void setReserved(const std::array& reserved); - /// Does nothing for this layer (WireGuard layer is always last) - void parseNextLayer() override - {} - /// @return Size of the header in bytes. size_t getHeaderLen() const override; @@ -121,6 +117,11 @@ namespace pcpp { return WireGuardMessageType::Unknown; } + + protected: + /// Does nothing for this layer (WireGuard layer is always last) + void doParseNextLayer(ParserConfiguration const& config) override + {} }; /// @class WireGuardHandshakeInitiationLayer diff --git a/Packet++/src/BgpLayer.cpp b/Packet++/src/BgpLayer.cpp index 003dfb44ac..0acdf29c6c 100644 --- a/Packet++/src/BgpLayer.cpp +++ b/Packet++/src/BgpLayer.cpp @@ -77,7 +77,7 @@ namespace pcpp } } - void BgpLayer::parseNextLayer() + void BgpLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen || headerLen == 0) diff --git a/Packet++/src/CiscoHdlcLayer.cpp b/Packet++/src/CiscoHdlcLayer.cpp index 7fac1ae5cd..9d8d9cbc70 100644 --- a/Packet++/src/CiscoHdlcLayer.cpp +++ b/Packet++/src/CiscoHdlcLayer.cpp @@ -43,7 +43,7 @@ namespace pcpp } } - void CiscoHdlcLayer::parseNextLayer() + void CiscoHdlcLayer::doParseNextLayer(ParserConfiguration const& config) { auto payload = m_Data + sizeof(cisco_hdlc_header); auto payloadLen = m_DataLen - sizeof(cisco_hdlc_header); diff --git a/Packet++/src/CotpLayer.cpp b/Packet++/src/CotpLayer.cpp index 1183df5f13..fd75f6546b 100644 --- a/Packet++/src/CotpLayer.cpp +++ b/Packet++/src/CotpLayer.cpp @@ -63,7 +63,7 @@ namespace pcpp return data[1] == 0xf0 && data[0] == 2; } - void CotpLayer::parseNextLayer() + void CotpLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen) diff --git a/Packet++/src/DoIpLayer.cpp b/Packet++/src/DoIpLayer.cpp index 2944888109..71a0018176 100644 --- a/Packet++/src/DoIpLayer.cpp +++ b/Packet++/src/DoIpLayer.cpp @@ -382,7 +382,7 @@ namespace pcpp setPayloadLength(length); } - void DoIpLayer::parseNextLayer() + void DoIpLayer::doParseNextLayer(ParserConfiguration const& config) { if (getPayloadType() == DoIpPayloadTypes::DIAGNOSTIC_MESSAGE) { diff --git a/Packet++/src/EthDot3Layer.cpp b/Packet++/src/EthDot3Layer.cpp index 35d6d48b14..17400791b8 100644 --- a/Packet++/src/EthDot3Layer.cpp +++ b/Packet++/src/EthDot3Layer.cpp @@ -21,7 +21,7 @@ namespace pcpp m_Protocol = Ethernet; } - void EthDot3Layer::parseNextLayer() + void EthDot3Layer::doParseNextLayer(ParserConfiguration const& config) { if (m_DataLen <= sizeof(ether_dot3_header)) return; diff --git a/Packet++/src/EthLayer.cpp b/Packet++/src/EthLayer.cpp index 40954d3174..349b0cfa1d 100644 --- a/Packet++/src/EthLayer.cpp +++ b/Packet++/src/EthLayer.cpp @@ -27,7 +27,7 @@ namespace pcpp m_Protocol = Ethernet; } - void EthLayer::parseNextLayer() + void EthLayer::doParseNextLayer(ParserConfiguration const& config) { if (m_DataLen <= sizeof(ether_header)) return; diff --git a/Packet++/src/GreLayer.cpp b/Packet++/src/GreLayer.cpp index 2bf2868b47..e5d4579b79 100644 --- a/Packet++/src/GreLayer.cpp +++ b/Packet++/src/GreLayer.cpp @@ -192,7 +192,7 @@ namespace pcpp return true; } - void GreLayer::parseNextLayer() + void GreLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen) @@ -563,7 +563,7 @@ namespace pcpp header->control = control; } - void PPP_PPTPLayer::parseNextLayer() + void PPP_PPTPLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen) diff --git a/Packet++/src/GtpLayer.cpp b/Packet++/src/GtpLayer.cpp index 97129b1418..b3523209c0 100644 --- a/Packet++/src/GtpLayer.cpp +++ b/Packet++/src/GtpLayer.cpp @@ -559,7 +559,7 @@ namespace pcpp return header->messageType != PCPP_GTP_V1_GPDU_MESSAGE_TYPE; } - void GtpV1Layer::parseNextLayer() + void GtpV1Layer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (headerLen < sizeof(gtpv1_header)) @@ -1302,7 +1302,7 @@ namespace pcpp return GtpV2InformationElement(newInfoElementPtr); } - void GtpV2Layer::parseNextLayer() + void GtpV2Layer::doParseNextLayer(ParserConfiguration const& config) { auto headerLen = getHeaderLen(); if (m_DataLen <= headerLen) diff --git a/Packet++/src/IPSecLayer.cpp b/Packet++/src/IPSecLayer.cpp index 5f26dc0468..3341b2af96 100644 --- a/Packet++/src/IPSecLayer.cpp +++ b/Packet++/src/IPSecLayer.cpp @@ -51,7 +51,7 @@ namespace pcpp return byteArrayToHexString(bytes, getICVLength()); } - void AuthenticationHeaderLayer::parseNextLayer() + void AuthenticationHeaderLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen) @@ -112,7 +112,7 @@ namespace pcpp return be32toh(getESPHeader()->sequenceNumber); } - void ESPLayer::parseNextLayer() + void ESPLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen) diff --git a/Packet++/src/IPv4Layer.cpp b/Packet++/src/IPv4Layer.cpp index 4a09134b96..c83debcc4c 100644 --- a/Packet++/src/IPv4Layer.cpp +++ b/Packet++/src/IPv4Layer.cpp @@ -244,7 +244,7 @@ namespace pcpp return *this; } - void IPv4Layer::parseNextLayer() + void IPv4Layer::doParseNextLayer(ParserConfiguration const& config) { size_t hdrLen = getHeaderLen(); if (m_DataLen <= hdrLen || hdrLen == 0) diff --git a/Packet++/src/IPv6Layer.cpp b/Packet++/src/IPv6Layer.cpp index fb17d09051..dc1f44f995 100644 --- a/Packet++/src/IPv6Layer.cpp +++ b/Packet++/src/IPv6Layer.cpp @@ -193,7 +193,7 @@ namespace pcpp return getExtensionOfType() != nullptr; } - void IPv6Layer::parseNextLayer() + void IPv6Layer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); diff --git a/Packet++/src/IcmpLayer.cpp b/Packet++/src/IcmpLayer.cpp index 171560aaef..6d1554fc66 100644 --- a/Packet++/src/IcmpLayer.cpp +++ b/Packet++/src/IcmpLayer.cpp @@ -561,7 +561,7 @@ namespace pcpp return header; } - void IcmpLayer::parseNextLayer() + void IcmpLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); diff --git a/Packet++/src/LLCLayer.cpp b/Packet++/src/LLCLayer.cpp index 5cfe5d84da..096ccda677 100644 --- a/Packet++/src/LLCLayer.cpp +++ b/Packet++/src/LLCLayer.cpp @@ -24,7 +24,7 @@ namespace pcpp header->control = control; } - void LLCLayer::parseNextLayer() + void LLCLayer::doParseNextLayer(ParserConfiguration const& config) { if (m_DataLen <= sizeof(llc_header)) return; diff --git a/Packet++/src/LdapLayer.cpp b/Packet++/src/LdapLayer.cpp index ebb0e844d5..e112efb27c 100644 --- a/Packet++/src/LdapLayer.cpp +++ b/Packet++/src/LdapLayer.cpp @@ -360,7 +360,7 @@ namespace pcpp return LdapOperationType::fromUintValue(tagType); } - void LdapLayer::parseNextLayer() + void LdapLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen || headerLen == 0) diff --git a/Packet++/src/MplsLayer.cpp b/Packet++/src/MplsLayer.cpp index fe57a68529..bf8be778e6 100644 --- a/Packet++/src/MplsLayer.cpp +++ b/Packet++/src/MplsLayer.cpp @@ -101,7 +101,7 @@ namespace pcpp return true; } - void MplsLayer::parseNextLayer() + void MplsLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen < headerLen + 1) diff --git a/Packet++/src/NflogLayer.cpp b/Packet++/src/NflogLayer.cpp index 3dc6f94480..9869c744b2 100644 --- a/Packet++/src/NflogLayer.cpp +++ b/Packet++/src/NflogLayer.cpp @@ -37,7 +37,7 @@ namespace pcpp return tlv; } - void NflogLayer::parseNextLayer() + void NflogLayer::doParseNextLayer(ParserConfiguration const& config) { if (m_DataLen <= sizeof(nflog_header)) { diff --git a/Packet++/src/NullLoopbackLayer.cpp b/Packet++/src/NullLoopbackLayer.cpp index d3ea72a2cf..559086d304 100644 --- a/Packet++/src/NullLoopbackLayer.cpp +++ b/Packet++/src/NullLoopbackLayer.cpp @@ -50,7 +50,7 @@ namespace pcpp *m_Data = family; } - void NullLoopbackLayer::parseNextLayer() + void NullLoopbackLayer::doParseNextLayer(ParserConfiguration const& config) { uint8_t* payload = m_Data + sizeof(uint32_t); size_t payloadLen = m_DataLen - sizeof(uint32_t); diff --git a/Packet++/src/PPPoELayer.cpp b/Packet++/src/PPPoELayer.cpp index f8d0e88388..517a9b9fc2 100644 --- a/Packet++/src/PPPoELayer.cpp +++ b/Packet++/src/PPPoELayer.cpp @@ -40,7 +40,7 @@ namespace pcpp /// PPPoESessionLayer /// ~~~~~~~~~~~~~~~~~ - void PPPoESessionLayer::parseNextLayer() + void PPPoESessionLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen) diff --git a/Packet++/src/ParserConfig.cpp b/Packet++/src/ParserConfig.cpp new file mode 100644 index 0000000000..e50b82b490 --- /dev/null +++ b/Packet++/src/ParserConfig.cpp @@ -0,0 +1,197 @@ +#include "ParserConfig.h" + +namespace pcpp +{ + PortMapper PortMapper::makeDefaultPortMapper() + { + PortMapper mapper; + // Add HTTP port mappings + mapper.addPortMapping(PortPair::fromDst(80), HTTPRequest, false); + mapper.addPortMapping(PortPair::fromSrc(80), HTTPResponse, false); + mapper.addPortMapping(PortPair::fromDst(8080), HTTPRequest, false); + mapper.addPortMapping(PortPair::fromSrc(8080), HTTPResponse, false); + + // SSL and TLS port mappings + mapper.addPortMapping(PortPair::fromDst(443), SSL, true); // HTTPS + mapper.addPortMapping(PortPair::fromDst(261), SSL, true); // NSIIOPS + mapper.addPortMapping(PortPair::fromDst(448), SSL, true); // DDM-SSL + mapper.addPortMapping(PortPair::fromDst(465), SSL, true); // SMTPS + mapper.addPortMapping(PortPair::fromDst(563), SSL, true); // NNTPS + mapper.addPortMapping(PortPair::fromDst(614), SSL, true); // SSHELL + mapper.addPortMapping(PortPair::fromDst(636), SSL, true); // LDAPS + mapper.addPortMapping(PortPair::fromDst(989), SSL, true); // FTPS - data + mapper.addPortMapping(PortPair::fromDst(990), SSL, true); // FTPS - control + mapper.addPortMapping(PortPair::fromDst(992), SSL, true); // Telnet over TLS/SSL + mapper.addPortMapping(PortPair::fromDst(993), SSL, true); // IMAPS + mapper.addPortMapping(PortPair::fromDst(994), SSL, true); // IRCS + mapper.addPortMapping(PortPair::fromDst(995), SSL, true); // POP3S + + // SIP port mappings + mapper.addPortMapping(PortPair::fromDst(5060), SIP, true); // SIP over UDP / TCP + mapper.addPortMapping(PortPair::fromDst(5061), SIP, true); // SIP over TLS + + // BGP port mappings + mapper.addPortMapping(PortPair::fromDst(179), BGP, true); // BGP over TCP + + // SSH port mappings + mapper.addPortMapping(PortPair::fromDst(22), SSH, true); // SSH over TCP + + // DNS port mappings + mapper.addPortMapping(PortPair::fromDst(53), DNS, true); // DNS over TCP/UDP + mapper.addPortMapping(PortPair::fromDst(5353), DNS, true); // mDNS + mapper.addPortMapping(PortPair::fromDst(5355), DNS, true); // LLMNR + + // Telnet port mappings + mapper.addPortMapping(PortPair::fromDst(23), Telnet, true); // Telnet over TCP + + // FTP port mappings + // FTP Control parses to FTPRequest and FTPResponse, but only one FTP protocol type is defined. + // The specific parsing determined based on if the port is src or dst. + // A port pairing (21, 21) for example is UB. + mapper.addPortMapping(PortPair{ 21, 21 }, UnknownProtocol, false); // Symmetrical connection is UB + mapper.addPortMapping(PortPair::fromSrc(21), FTPControl, true); // FTP control + mapper.addPortMapping(PortPair::fromDst(20), FTPData, true); // FTP data + + // SomeIP port mappings + mapper.addPortMapping(PortPair::fromDst(30490), SomeIP, true); // SomeIP over UDP or TCP + + // Tpkt port mappings + mapper.addPortMapping(PortPair::fromDst(102), TPKT, true); // TPKT over TCP + + // Smtp port mappings + // NOTE: Symmetrical mapping but decodes to SMTPRequest and SMTPResponse + // A port pairing (25, 25) for example is UB. + mapper.addPortMapping(PortPair{ 25, 25 }, UnknownProtocol, false); // Symmetrical connection is UB + mapper.addPortMapping(PortPair::fromDst(25), SMTP, true); // SMTP over TCP + mapper.addPortMapping(PortPair::fromDst(587), SMTP, true); // SMTP over TCP (submission) + + // LDAP port mappings + mapper.addPortMapping(PortPair::fromDst(389), LDAP, true); // LDAP over TCP + + // GTP port mappings + mapper.addPortMapping(PortPair::fromDst(2152), GTPv1, true); // GTP-U over UDP + + // Note: GTP v1 and v2 both utilize port (2123) for GTP-C over UDP / TCP. + // Parser implementations must determine the version based on the packet content. + mapper.addPortMapping(PortPair::fromDst(2123), GTP, true); // GTP-C over UDP / TCP (v2 only) + + // DHCP port mappings + mapper.addPortMapping(PortPair{ 67, 67 }, DHCP, true); // DHCP over UDP + mapper.addPortMapping(PortPair{ 68, 67 }, DHCP, true); // DHCP over UDP (client to server) + + // DHCPv6 port mappings + mapper.addPortMapping(PortPair::fromDst(546), DHCPv6, true); // DHCPv6 over UDP + mapper.addPortMapping(PortPair::fromDst(547), DHCPv6, true); // DHCPv6 over UDP + + // VXLAN port mappings + mapper.addPortMapping(PortPair::fromDst(4789), VXLAN, false); // VXLAN over UDP + + // Radius port mappings + mapper.addPortMapping(PortPair::fromDst(1812), Radius, true); // RADIUS over UDP + mapper.addPortMapping(PortPair::fromDst(1813), Radius, true); // RADIUS accounting over UDP + mapper.addPortMapping(PortPair::fromDst(3799), Radius, true); // RADIUS over TCP + + // NTP port mappings + mapper.addPortMapping(PortPair::fromDst(123), NTP, true); // NTP over UDP + + // DoIP port mappings + mapper.addPortMapping(PortPair::fromDst(13400), DOIP, true); // DoIP over TCP/UDP + mapper.addPortMapping(PortPair::fromDst(3496), DOIP, true); // DoIP over TLS + + // Wake-on-LAN port mappings + mapper.addPortMapping(PortPair::fromDst(9), WakeOnLan, false); // Wake-on-LAN over UDP + mapper.addPortMapping(PortPair::fromDst(7), WakeOnLan, false); // Wake-on-LAN over UDP + mapper.addPortMapping(PortPair::fromDst(0), WakeOnLan, false); // Wake-on-LAN over UDP (broadcast) + + // WireGuard port mappings + mapper.addPortMapping(PortPair::fromDst(51820), WireGuard, true); // WireGuard over UDP + + return mapper; + } + + void PortMapper::addPortMapping(PortPair port, ProtocolTypeFamily protocol, bool symmetrical) + { + if (port == PortPair()) + { + throw std::invalid_argument("PortPair cannot be empty (both src and dst ports are 0)"); + } + + auto insertResult = m_PortToProtocolMap.insert({ port, protocol }); + if (!insertResult.second) + { + PCPP_LOG_WARN("Port " << port << " is already mapped to protocol " + << std::to_string(insertResult.first->second) << ", updating to " << protocol); + } + insertResult.first->second = protocol; // Update the protocol if it already exists + + if (symmetrical && (port.hasWildcards() || port.portSrc() != port.portDst())) + { + // Add the symmetrical mapping + addPortMapping(port.withSwappedPorts(), protocol, false); + } + } + + void PortMapper::removePortMapping(PortPair port, bool symmetrical) + { + auto it = m_PortToProtocolMap.find(port); + if (it != m_PortToProtocolMap.end()) + { + m_PortToProtocolMap.erase(it); + } + else + { + PCPP_LOG_DEBUG("Port " << port << " not found in port mapper, nothing to remove"); + } + + if (symmetrical && (port.hasWildcards() || port.portSrc() != port.portDst())) + { + // Remove the symmetrical mapping + removePortMapping(port.withSwappedPorts(), false); + } + } + + ProtocolTypeFamily PortMapper::getProtocolByPortPair(PortPair port) const + { + auto it = m_PortToProtocolMap.find(port); + if (it != m_PortToProtocolMap.end()) + { + return it->second; + } + + return UnknownProtocol; // Return UnknownProtocol if exact match not found + } + + std::array PortMapper::getMatchMatrix(PortPair port) const + { + std::array protocols = { UnknownProtocol, UnknownProtocol, UnknownProtocol }; + // Check for exact match + auto it = m_PortToProtocolMap.find(port); + if (it != m_PortToProtocolMap.end()) + { + protocols[0] = it->second; // Full match + return protocols; + } + + // Check for src port match + it = m_PortToProtocolMap.find(port.withAnyDst()); + if (it != m_PortToProtocolMap.end()) + { + protocols[1] = it->second; // Src port match + } + + // Check for dst port match + it = m_PortToProtocolMap.find(port.withAnySrc()); + if (it != m_PortToProtocolMap.end()) + { + protocols[2] = it->second; // Dst port match + } + return protocols; + } + + ParserConfiguration ParserConfiguration::makeDefaultConfiguration() + { + ParserConfiguration config; + config.portMapper = PortMapper::makeDefaultPortMapper(); + return config; + } +} // namespace pcpp diff --git a/Packet++/src/SSHLayer.cpp b/Packet++/src/SSHLayer.cpp index 7181cf6f6d..7ef1db2029 100644 --- a/Packet++/src/SSHLayer.cpp +++ b/Packet++/src/SSHLayer.cpp @@ -28,7 +28,7 @@ namespace pcpp return new SSHEncryptedMessage(data, dataLen, prevLayer, packet); } - void SSHLayer::parseNextLayer() + void SSHLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen) diff --git a/Packet++/src/SSLLayer.cpp b/Packet++/src/SSLLayer.cpp index e83b789c07..970d2b4a0d 100644 --- a/Packet++/src/SSLLayer.cpp +++ b/Packet++/src/SSLLayer.cpp @@ -88,7 +88,7 @@ namespace pcpp return len; } - void SSLLayer::parseNextLayer() + void SSLLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen) diff --git a/Packet++/src/SipLayer.cpp b/Packet++/src/SipLayer.cpp index ac9acb1289..35b6444952 100644 --- a/Packet++/src/SipLayer.cpp +++ b/Packet++/src/SipLayer.cpp @@ -64,7 +64,7 @@ namespace pcpp return contentLengthField; } - void SipLayer::parseNextLayer() + void SipLayer::doParseNextLayer(ParserConfiguration const& config) { if (getLayerPayloadSize() == 0) return; diff --git a/Packet++/src/Sll2Layer.cpp b/Packet++/src/Sll2Layer.cpp index 8bcbd3cedd..219746f832 100644 --- a/Packet++/src/Sll2Layer.cpp +++ b/Packet++/src/Sll2Layer.cpp @@ -56,7 +56,7 @@ namespace pcpp return setLinkLayerAddr(macAddrAsArr, 6); } - void Sll2Layer::parseNextLayer() + void Sll2Layer::doParseNextLayer(ParserConfiguration const& config) { if (m_DataLen <= sizeof(sll2_header)) return; diff --git a/Packet++/src/SllLayer.cpp b/Packet++/src/SllLayer.cpp index 9e2141f9c1..3616dca436 100644 --- a/Packet++/src/SllLayer.cpp +++ b/Packet++/src/SllLayer.cpp @@ -48,7 +48,7 @@ namespace pcpp return setLinkLayerAddr(macAddrAsArr, 6); } - void SllLayer::parseNextLayer() + void SllLayer::doParseNextLayer(ParserConfiguration const& config) { if (m_DataLen <= sizeof(sll_header)) return; diff --git a/Packet++/src/SomeIpLayer.cpp b/Packet++/src/SomeIpLayer.cpp index baf8a06e7b..7cb3158026 100644 --- a/Packet++/src/SomeIpLayer.cpp +++ b/Packet++/src/SomeIpLayer.cpp @@ -255,7 +255,7 @@ namespace pcpp payloadLength); } - void SomeIpLayer::parseNextLayer() + void SomeIpLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen) diff --git a/Packet++/src/StpLayer.cpp b/Packet++/src/StpLayer.cpp index 0eda516624..738b31fce3 100644 --- a/Packet++/src/StpLayer.cpp +++ b/Packet++/src/StpLayer.cpp @@ -79,7 +79,7 @@ namespace pcpp setType(0x80); } - void StpTopologyChangeBPDULayer::parseNextLayer() + void StpTopologyChangeBPDULayer::doParseNextLayer(ParserConfiguration const& config) { if (m_DataLen > sizeof(stp_tcn_bpdu)) m_NextLayer = new PayloadLayer(m_Data, m_DataLen - sizeof(stp_tcn_bpdu), this, m_Packet); @@ -224,7 +224,7 @@ namespace pcpp getStpConfHeader()->forwardDelay = value; } - void StpConfigurationBPDULayer::parseNextLayer() + void StpConfigurationBPDULayer::doParseNextLayer(ParserConfiguration const& config) { if (m_DataLen > sizeof(stp_conf_bpdu)) m_NextLayer = new PayloadLayer(m_Data, m_DataLen - sizeof(stp_conf_bpdu), this, m_Packet); @@ -239,7 +239,7 @@ namespace pcpp setType(0x2); } - void RapidStpLayer::parseNextLayer() + void RapidStpLayer::doParseNextLayer(ParserConfiguration const& config) { if (m_DataLen > sizeof(rstp_conf_bpdu)) m_NextLayer = new PayloadLayer(m_Data, m_DataLen - sizeof(rstp_conf_bpdu), this, m_Packet); diff --git a/Packet++/src/TcpLayer.cpp b/Packet++/src/TcpLayer.cpp index 0250b5e7b0..fe9818df88 100644 --- a/Packet++/src/TcpLayer.cpp +++ b/Packet++/src/TcpLayer.cpp @@ -356,7 +356,7 @@ namespace pcpp return *this; } - void TcpLayer::parseNextLayer() + void TcpLayer::doParseNextLayer(ParserConfiguration const& config) { const size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen) @@ -368,12 +368,14 @@ namespace pcpp const uint16_t portSrc = getSrcPort(); const char* payloadChar = reinterpret_cast(payload); - if (HttpMessage::isHttpPort(portDst) && + auto const& portMapper = config.portMapper; + + if (portMapper.matchesPortAndProtocol(PortPair::fromDst(portDst), HTTPRequest) && HttpRequestFirstLine::parseMethod(payloadChar, payloadLen) != HttpRequestLayer::HttpMethodUnknown) { constructNextLayer(payload, payloadLen, m_Packet); } - else if (HttpMessage::isHttpPort(portSrc) && + else if (portMapper.matchesPortAndProtocol(PortPair::fromSrc(portSrc), HTTPResponse) && HttpResponseFirstLine::parseVersion(payloadChar, payloadLen) != HttpVersion::HttpVersionUnknown && !HttpResponseFirstLine::parseStatusCode(payloadChar, payloadLen).isUnsupportedCode()) { diff --git a/Packet++/src/TextBasedProtocol.cpp b/Packet++/src/TextBasedProtocol.cpp index 1d35996384..5f3e764b36 100644 --- a/Packet++/src/TextBasedProtocol.cpp +++ b/Packet++/src/TextBasedProtocol.cpp @@ -403,7 +403,7 @@ namespace pcpp return result; } - void TextBasedProtocolMessage::parseNextLayer() + void TextBasedProtocolMessage::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen) diff --git a/Packet++/src/TpktLayer.cpp b/Packet++/src/TpktLayer.cpp index c3fe3a9ff7..057a3ab57b 100644 --- a/Packet++/src/TpktLayer.cpp +++ b/Packet++/src/TpktLayer.cpp @@ -54,7 +54,7 @@ namespace pcpp return "TPKT Layer, version: " + versionStream.str() + ", length: " + lengthStream.str(); } - void TpktLayer::parseNextLayer() + void TpktLayer::doParseNextLayer(ParserConfiguration const& config) { size_t headerLen = getHeaderLen(); if (m_DataLen <= headerLen) diff --git a/Packet++/src/UdpLayer.cpp b/Packet++/src/UdpLayer.cpp index 478d2893a3..d83a111746 100644 --- a/Packet++/src/UdpLayer.cpp +++ b/Packet++/src/UdpLayer.cpp @@ -90,7 +90,7 @@ namespace pcpp return checksumRes; } - void UdpLayer::parseNextLayer() + void UdpLayer::doParseNextLayer(ParserConfiguration const& config) { if (m_DataLen <= sizeof(udphdr)) return; @@ -101,59 +101,147 @@ namespace pcpp uint8_t* udpData = m_Data + sizeof(udphdr); size_t udpDataLen = m_DataLen - sizeof(udphdr); - if (DhcpLayer::isDhcpPorts(portSrc, portDst)) - m_NextLayer = new DhcpLayer(udpData, udpDataLen, this, m_Packet); - else if (VxlanLayer::isVxlanPort(portDst)) - m_NextLayer = new VxlanLayer(udpData, udpDataLen, this, m_Packet); - else if (DnsLayer::isDataValid(udpData, udpDataLen) && - (DnsLayer::isDnsPort(portDst) || DnsLayer::isDnsPort(portSrc))) - m_NextLayer = new DnsLayer(udpData, udpDataLen, this, m_Packet); - else if (SipLayer::isSipPort(portDst) || SipLayer::isSipPort(portSrc)) - { - if (SipRequestFirstLine::parseMethod((char*)udpData, udpDataLen) != SipRequestLayer::SipMethodUnknown) - m_NextLayer = new SipRequestLayer(udpData, udpDataLen, this, m_Packet); - else if (SipResponseFirstLine::parseStatusCode((char*)udpData, udpDataLen) != - SipResponseLayer::SipStatusCodeUnknown && - SipResponseFirstLine::parseVersion((char*)udpData, udpDataLen) != "") - m_NextLayer = new SipResponseLayer(udpData, udpDataLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); - } - else if ((RadiusLayer::isRadiusPort(portDst) || RadiusLayer::isRadiusPort(portSrc)) && - RadiusLayer::isDataValid(udpData, udpDataLen)) - m_NextLayer = new RadiusLayer(udpData, udpDataLen, this, m_Packet); - else if ((GtpV1Layer::isGTPv1Port(portDst) || GtpV1Layer::isGTPv1Port(portSrc)) && - GtpV1Layer::isGTPv1(udpData, udpDataLen)) - m_NextLayer = new GtpV1Layer(udpData, udpDataLen, this, m_Packet); - else if ((GtpV2Layer::isGTPv2Port(portDst) || GtpV2Layer::isGTPv2Port(portSrc)) && - GtpV2Layer::isDataValid(udpData, udpDataLen)) - m_NextLayer = new GtpV2Layer(udpData, udpDataLen, this, m_Packet); - else if ((DhcpV6Layer::isDhcpV6Port(portSrc) || DhcpV6Layer::isDhcpV6Port(portDst)) && - (DhcpV6Layer::isDataValid(udpData, udpDataLen))) - m_NextLayer = new DhcpV6Layer(udpData, udpDataLen, this, m_Packet); - else if ((NtpLayer::isNTPPort(portSrc) || NtpLayer::isNTPPort(portDst)) && - NtpLayer::isDataValid(udpData, udpDataLen)) - m_NextLayer = new NtpLayer(udpData, udpDataLen, this, m_Packet); - else if ((DoIpLayer::isDoIpPort(portSrc) || DoIpLayer::isDoIpPort(portDst)) && - (DoIpLayer::isDataValid(udpData, udpDataLen))) + // Queries the port mapper for protocol mappings based on the source and destination ports + // The returned array protocol family for exact match, source only match, destination only match. + // The first protocol family that passes secondary validation (if any) will be used to construct the next layer. + auto const portMatrix = config.portMapper.getMatchMatrix(portSrc, portDst); + + for (auto protoFamily : portMatrix) { - m_NextLayer = DoIpLayer::parseDoIpLayer(udpData, udpDataLen, this, m_Packet); - if (!m_NextLayer) - constructNextLayer(udpData, udpDataLen, m_Packet); + // If the protocol family is UnknownProtocol, skip all other checks + if (protoFamily == UnknownProtocol) + continue; + + switch (protoFamily) + { + case DHCP: + { + constructNextLayer(udpData, udpDataLen, m_Packet); + break; + } + case VXLAN: + { + constructNextLayer(udpData, udpDataLen, m_Packet); + break; + } + case DNS: + { + if (DnsLayer::isDataValid(udpData, udpDataLen)) + { + constructNextLayer(udpData, udpDataLen, m_Packet); + } + break; + } + case SIPRequest: + case SIPResponse: + case SIP: + { + if (SipRequestFirstLine::parseMethod((char*)udpData, udpDataLen) != SipRequestLayer::SipMethodUnknown) + { + constructNextLayer(udpData, udpDataLen, m_Packet); + } + else if (SipResponseFirstLine::parseStatusCode((char*)udpData, udpDataLen) != + SipResponseLayer::SipStatusCodeUnknown && + SipResponseFirstLine::parseVersion((char*)udpData, udpDataLen) != "") + { + constructNextLayer(udpData, udpDataLen, m_Packet); + } + else + { + // todo: If the data is not a valid SIP request or response, should be instead try to continue + // matching? + constructNextLayer(udpData, udpDataLen, m_Packet); + } + break; + } + case Radius: + { + if (RadiusLayer::isDataValid(udpData, udpDataLen)) + { + constructNextLayer(udpData, udpDataLen, m_Packet); + } + break; + } + case GTPv1: + case GTPv2: + case GTP: + { + // GTP can be either v1 or v2 + if (GtpV1Layer::isGTPv1(udpData, udpDataLen)) + { + constructNextLayer(udpData, udpDataLen, m_Packet); + } + else if (GtpV2Layer::isDataValid(udpData, udpDataLen)) + { + constructNextLayer(udpData, udpDataLen, m_Packet); + } + break; + } + case DHCPv6: + { + if (DhcpV6Layer::isDataValid(udpData, udpDataLen)) + { + constructNextLayer(udpData, udpDataLen, m_Packet); + } + break; + } + case NTP: + { + if (NtpLayer::isDataValid(udpData, udpDataLen)) + { + constructNextLayer(udpData, udpDataLen, m_Packet); + } + break; + } + case DOIP: + { + if (DoIpLayer::isDataValid(udpData, udpDataLen)) + { + m_NextLayer = DoIpLayer::parseDoIpLayer(udpData, udpDataLen, this, m_Packet); + if (!m_NextLayer) + constructNextLayer(udpData, udpDataLen, m_Packet); + } + break; + } + case SomeIP: + { + setNextLayer(SomeIpLayer::parseSomeIpLayer(udpData, udpDataLen, this, m_Packet)); + break; + } + case WakeOnLan: + { + if (WakeOnLanLayer::isDataValid(udpData, udpDataLen)) + { + constructNextLayer(udpData, udpDataLen, m_Packet); + } + break; + } + case WireGuard: + { + if (WireGuardLayer::isDataValid(udpData, udpDataLen)) + { + setNextLayer(WireGuardLayer::parseWireGuardLayer(udpData, udpDataLen, this, m_Packet)); + if (!m_NextLayer) + { + // If parsing failed, fallback to PayloadLayer + // todo: Maybe continue matching partial matches instead? + constructNextLayer(udpData, udpDataLen, m_Packet); + } + } + break; + } + } + + // If we already have a next layer, we don't need to parse further + if (hasNextLayer()) + break; } - else if (SomeIpLayer::isSomeIpPort(portSrc) || SomeIpLayer::isSomeIpPort(portDst)) - m_NextLayer = SomeIpLayer::parseSomeIpLayer(udpData, udpDataLen, this, m_Packet); - else if ((WakeOnLanLayer::isWakeOnLanPort(portDst) && WakeOnLanLayer::isDataValid(udpData, udpDataLen))) - m_NextLayer = new WakeOnLanLayer(udpData, udpDataLen, this, m_Packet); - else if ((WireGuardLayer::isWireGuardPorts(portDst, portSrc) && - WireGuardLayer::isDataValid(udpData, udpDataLen))) + + if (!hasNextLayer()) { - m_NextLayer = WireGuardLayer::parseWireGuardLayer(udpData, udpDataLen, this, m_Packet); - if (!m_NextLayer) - m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); + // No specific layer matched, set as PayloadLayer + constructNextLayer(udpData, udpDataLen, m_Packet); } - else - m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); } void UdpLayer::computeCalculateFields() diff --git a/Packet++/src/VlanLayer.cpp b/Packet++/src/VlanLayer.cpp index f12ca798ae..6c941c8bbc 100644 --- a/Packet++/src/VlanLayer.cpp +++ b/Packet++/src/VlanLayer.cpp @@ -59,7 +59,7 @@ namespace pcpp getVlanHeader()->vlan = htobe16((be16toh(getVlanHeader()->vlan) & (~(7 << 13))) | ((priority & 7) << 13)); } - void VlanLayer::parseNextLayer() + void VlanLayer::doParseNextLayer(ParserConfiguration const& config) { if (m_DataLen <= sizeof(vlan_header)) return; diff --git a/Packet++/src/VxlanLayer.cpp b/Packet++/src/VxlanLayer.cpp index 6001b4197b..96313ea4e0 100644 --- a/Packet++/src/VxlanLayer.cpp +++ b/Packet++/src/VxlanLayer.cpp @@ -49,7 +49,7 @@ namespace pcpp return "VXLAN Layer"; } - void VxlanLayer::parseNextLayer() + void VxlanLayer::doParseNextLayer(ParserConfiguration const& config) { if (m_DataLen <= sizeof(vxlan_header)) return; diff --git a/Tests/Packet++Test/Tests/SomeIpTests.cpp b/Tests/Packet++Test/Tests/SomeIpTests.cpp index b02b89904c..fdbcab1674 100644 --- a/Tests/Packet++Test/Tests/SomeIpTests.cpp +++ b/Tests/Packet++Test/Tests/SomeIpTests.cpp @@ -24,6 +24,34 @@ class SomeIpTeardown } }; +class SomeIpScopedPortRegistration +{ +public: + explicit SomeIpScopedPortRegistration(pcpp::PortPair portPair) : m_RegisteredPort(portPair) + { + auto& portMapper = pcpp::ParserConfiguration::getDefault().portMapper; + m_PreviousMapping = portMapper.getProtocolByPortPair(m_RegisteredPort); + portMapper.addPortMapping(m_RegisteredPort, pcpp::SomeIP, true); + } + ~SomeIpScopedPortRegistration() + { + auto& portMapper = pcpp::ParserConfiguration::getDefault().portMapper; + + if (m_PreviousMapping != pcpp::UnknownProtocol) + { + portMapper.addPortMapping(m_RegisteredPort, m_PreviousMapping, true); + } + else + { + portMapper.removePortMapping(m_RegisteredPort, true); + } + } + +private: + pcpp::PortPair m_RegisteredPort; + pcpp::ProtocolTypeFamily m_PreviousMapping; +}; + PTF_TEST_CASE(SomeIpPortTest) { // cppcheck-suppress unusedVariable @@ -52,6 +80,9 @@ PTF_TEST_CASE(SomeIpParsingTest) // cppcheck-suppress unusedVariable SomeIpTeardown someIpTeardown; + + // Add port mapping for SOME/IP + SomeIpScopedPortRegistration someIpPortRegistration(pcpp::PortPair::fromDst(29180)); pcpp::SomeIpLayer::addSomeIpPort(29180); auto rawPacket1 = createPacketFromHexResource("PacketExamples/someip.dat"); @@ -124,6 +155,9 @@ PTF_TEST_CASE(SomeIpParsingTest) PTF_ASSERT_EQUAL(someIpLayer2_2->getPduPayload()[19], 0x14); PTF_ASSERT_EQUAL(someIpLayer2_2->toString(), "SOME/IP Layer, Service ID: 0x6060, Method ID: 0x410d, Length: 28"); PTF_ASSERT_NULL(someIpLayer2_2->getNextLayer()); + + // Remove port mapping for SOME/IP + pcpp::ParserConfiguration::getDefault().portMapper.removePortMapping(pcpp::PortPair::fromDst(29180), true); } PTF_TEST_CASE(SomeIpCreationTest) @@ -182,6 +216,7 @@ PTF_TEST_CASE(SomeIpTpParsingTest) // cppcheck-suppress unusedVariable SomeIpTeardown someIpTeardown; + SomeIpScopedPortRegistration someIpPortRegistration(pcpp::PortPair::fromDst(16832)); pcpp::SomeIpLayer::addSomeIpPort(16832); auto rawPacket1 = createPacketFromHexResource("PacketExamples/SomeIpTp1.dat"); diff --git a/Tests/Packet++Test/main.cpp b/Tests/Packet++Test/main.cpp index 5a6fd6cf1e..f2fde9d630 100644 --- a/Tests/Packet++Test/main.cpp +++ b/Tests/Packet++Test/main.cpp @@ -3,6 +3,7 @@ #include "PcppTestFrameworkRun.h" #include "TestDefinition.h" #include "Logger.h" +#include "ParserConfig.h" #include "../../Tests/Packet++Test/Utils/TestUtils.h" #include "Resources.h" @@ -95,6 +96,10 @@ int main(int argc, char* argv[]) // Disables context pooling to avoid false positives in the memory leak check, as the contexts persist in the pool. pcpp::Logger::getInstance().useContextPooling(false); + // Required to initialize the logger singleton before running tests. + // Lazy initialization will be detected as a false positive memory leak + pcpp::ParserConfiguration::getDefault(); + // cppcheck-suppress knownConditionTrueFalse if (skipMemLeakCheck) { diff --git a/Tests/Pcap++Test/main.cpp b/Tests/Pcap++Test/main.cpp index f37f34b761..601c4ca512 100644 --- a/Tests/Pcap++Test/main.cpp +++ b/Tests/Pcap++Test/main.cpp @@ -1,5 +1,6 @@ #include "PcapPlusPlusVersion.h" #include "Logger.h" +#include "ParserConfig.h" #include "PcppTestFrameworkRun.h" #include "TestDefinition.h" #include "Common/GlobalTestArgs.h" @@ -147,6 +148,10 @@ int main(int argc, char* argv[]) // Disables context pooling to avoid false positives in the memory leak check, as the contexts persist in the pool. pcpp::Logger::getInstance().useContextPooling(false); + // Required to initialize the logger singleton before running tests. + // Lazy initialization will be detected as a false positive memory leak + pcpp::ParserConfiguration::getDefault(); + // cppcheck-suppress knownConditionTrueFalse if (skipMemLeakCheck) {