From 6422d0614ec6573ea5fffced5ee0f7fe9828adea Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Wed, 10 Sep 2025 16:56:32 +0300 Subject: [PATCH 01/17] Added const qualified match methods to BfpFilterWrapper. --- Pcap++/header/PcapFilter.h | 72 ++++++++++++++++----- Pcap++/src/PcapFilter.cpp | 127 +++++++++++++++++++++++++++---------- 2 files changed, 150 insertions(+), 49 deletions(-) diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index e11a54a33d..19ef221295 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -78,31 +78,35 @@ namespace pcpp /// A wrapper class for BPF filtering. Enables setting a BPF filter and matching it against a packet class BpfFilterWrapper { - private: - std::string m_FilterStr; - LinkLayerType m_LinkType; - std::unique_ptr m_Program; + public: + /// @brief An enum with possible behaviours in case of link type missmatch + enum class LinkMissmatchBehaviour + { + /// @brief Attempt to recompile the filter with the new link type + RecompileFilter, + /// @brief Do not attempt to recompile the filter, just return false. + NoMatch + }; - void freeProgram(); + /// @brief Creates a new instance with no filter. + BpfFilterWrapper() = default; - public: - /// A c'tor for this class - BpfFilterWrapper(); + /// @brief Creates a new instance with the given filter string. + /// @param filter A filter in BPF syntax + /// @param linkType An optional parameter to set the filter's link type. The default is LINKTYPE_ETHERNET + /// @throws std::runtime_error if the filter could not be compiled + BpfFilterWrapper(std::string filter, LinkLayerType linkType = LINKTYPE_ETHERNET); - /// A copy constructor for this class. - /// @param[in] other The instance to copy from BpfFilterWrapper(const BpfFilterWrapper& other); - - /// A copy assignment operator for this class. - /// @param[in] other An instance of IPNetwork to assign - /// @return A reference to the assignee + BpfFilterWrapper(BpfFilterWrapper&&) noexcept = default; BpfFilterWrapper& operator=(const BpfFilterWrapper& other); + BpfFilterWrapper& operator=(BpfFilterWrapper&&) noexcept = default; /// Set a filter. This method receives a filter in BPF syntax (https://biot.com/capstats/bpf.html) and an /// optional link type, compiles them, and if compilation is successful it stores the filter. /// @param[in] filter A filter in BPF syntax /// @param[in] linkType An optional parameter to set the filter's link type. The default is LINKTYPE_ETHERNET - /// @return True if compilation is successful and filter is stored in side this object, false otherwise + /// @return True if compilation is successful and filter is stored inside this object, false otherwise bool setFilter(const std::string& filter, LinkLayerType linkType = LINKTYPE_ETHERNET); /// Match a packet with the filter stored in this object. If the filter is empty the method returns "true". @@ -124,6 +128,44 @@ namespace pcpp /// could not be compiled bool matchPacketWithFilter(const uint8_t* packetData, uint32_t packetDataLength, timespec packetTimestamp, uint16_t linkType); + + /// @brief Match a packet with the filter stored in this object. + /// + /// If the filter is empty the method returns "true". + /// If the link type of the raw packet is different than the one set in setFilter(): + /// - If onLinkMissmatch is set to RecompileFilter, the filter will be re-compiled and stored in the object. + /// - If onLinkMissmatch is set to NoMatch, the method will return "false" + /// + /// @param[in] rawPacket The raw packet to match the filter against + /// @param[in] onLinkMissmatch The behaviour in case of link type missmatch + /// @return True if the filter matches (or if it's empty). False otherwise + bool matches(const RawPacket& rawPacket, + LinkMissmatchBehaviour onLinkMissmatch = LinkMissmatchBehaviour::RecompileFilter) const; + + /// @brief Match a raw buffer of packet data against the filter stored in this object. + /// + /// If the filter is empty the method returns "true". + /// If the link type provided is different than the one set in setFilter(): + /// - If onLinkMissmatch is set to RecompileFilter, the filter will be re-compiled and stored in the object. + /// - If onLinkMissmatch is set to NoMatch, the method will return "false" + /// + /// @param[in] packetData A pointer to the raw packet data + /// @param[in] packetDataLength The length of the raw packet data in bytes + /// @param[in] timestamp Timestamp to be associated with the packet + /// @param[in] linkType The link type of the packet + /// @param[in] onLinkMissmatch The behaviour in case of link type missmatch + /// @return True if the filter matches (or if it's empty). False otherwise + bool matches(const uint8_t* packetData, uint32_t packetDataLength, timespec timestamp, uint16_t linkType, + LinkMissmatchBehaviour onLinkMissmatch = LinkMissmatchBehaviour::RecompileFilter) const; + + private: + using BpfProgramUPtr = std::unique_ptr; + + std::string m_FilterStr; + mutable LinkLayerType m_CachedProgramLinkType = LinkLayerType::LINKTYPE_ETHERNET; + mutable BpfProgramUPtr m_CachedProgram; + + static BpfProgramUPtr compileFilter(std::string const& filter, LinkLayerType linkType); }; /// @class GeneralFilter diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index 303ea76142..c5a0312f58 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -38,17 +38,23 @@ namespace pcpp } } // namespace internal - BpfFilterWrapper::BpfFilterWrapper() : m_LinkType(LinkLayerType::LINKTYPE_ETHERNET) - {} - - BpfFilterWrapper::BpfFilterWrapper(const BpfFilterWrapper& other) : BpfFilterWrapper() + BpfFilterWrapper::BpfFilterWrapper(std::string filter, LinkLayerType linkType) + : m_FilterStr(std::move(filter)), m_CachedProgramLinkType(linkType), + m_CachedProgram(compileFilter(m_FilterStr, linkType)) { - setFilter(other.m_FilterStr, other.m_LinkType); + if (!m_FilterStr.empty() && m_CachedProgram == nullptr) + { + throw std::runtime_error("Couldn't compile BPF filter: '" + m_FilterStr + "'"); + } } + BpfFilterWrapper::BpfFilterWrapper(const BpfFilterWrapper& other) + : BpfFilterWrapper(other.m_FilterStr, other.m_CachedProgramLinkType) + {} + BpfFilterWrapper& BpfFilterWrapper::operator=(const BpfFilterWrapper& other) { - setFilter(other.m_FilterStr, other.m_LinkType); + setFilter(other.m_FilterStr, other.m_CachedProgramLinkType); return *this; } @@ -56,64 +62,117 @@ namespace pcpp { if (filter.empty()) { - freeProgram(); + m_CachedProgram = nullptr; + m_FilterStr.clear(); return true; } - if (filter != m_FilterStr || linkType != m_LinkType) + if (filter != m_FilterStr || linkType != m_CachedProgramLinkType) { - auto pcap = std::unique_ptr(pcap_open_dead(linkType, DEFAULT_SNAPLEN)); - if (pcap == nullptr) + auto newProgram = compileFilter(filter, linkType); + if (newProgram == nullptr) { + PCPP_LOG_ERROR("Couldn't compile BPF filter: '" << filter << "'"); return false; } - auto newProg = std::make_unique(); - int ret = pcap_compile(pcap.get(), newProg.get(), filter.c_str(), 1, 0); - if (ret < 0) - { - return false; - } - - // Reassigns ownership of the bpf program to a new unique_ptr with a custom deleter as it now requires - // specialized cleanup. - m_Program = std::unique_ptr(newProg.release()); m_FilterStr = filter; - m_LinkType = linkType; + m_CachedProgram = std::move(newProgram); + m_CachedProgramLinkType = linkType; } return true; } - void BpfFilterWrapper::freeProgram() - { - m_Program = nullptr; - m_FilterStr.clear(); - } - bool BpfFilterWrapper::matchPacketWithFilter(const RawPacket* rawPacket) { - return matchPacketWithFilter(rawPacket->getRawData(), rawPacket->getRawDataLen(), - rawPacket->getPacketTimeStamp(), rawPacket->getLinkLayerType()); + if (rawPacket == nullptr) + { + PCPP_LOG_ERROR("Raw packet pointer is null"); + return false; + } + + return matches(*rawPacket); } bool BpfFilterWrapper::matchPacketWithFilter(const uint8_t* packetData, uint32_t packetDataLength, timespec packetTimestamp, uint16_t linkType) + { + return matches(packetData, packetDataLength, packetTimestamp, linkType); + } + + bool BpfFilterWrapper::matches(const RawPacket& rawPacket, LinkMissmatchBehaviour onLinkMissmatch) const + { + return matches(rawPacket.getRawData(), rawPacket.getRawDataLen(), rawPacket.getPacketTimeStamp(), + rawPacket.getLinkLayerType(), onLinkMissmatch); + } + + bool BpfFilterWrapper::matches(const uint8_t* packetData, uint32_t packetDataLength, timespec timestamp, + uint16_t linkType, LinkMissmatchBehaviour onLinkMissmatch) const { if (m_FilterStr.empty()) return true; - if (!setFilter(std::string(m_FilterStr), static_cast(linkType))) + // This should never happen, but just in case + if (m_CachedProgram == nullptr) { - return false; + throw std::runtime_error("No compiled BPF program available"); + } + + // Handle link type mismatch + if (linkType != static_cast(m_CachedProgramLinkType)) + { + switch (onLinkMissmatch) + { + case LinkMissmatchBehaviour::NoMatch: + { + return false; // Do not attempt to recompile, just return false + } + case LinkMissmatchBehaviour::RecompileFilter: + { + auto newProgram = compileFilter(m_FilterStr, static_cast(linkType)); + if (newProgram == nullptr) + { + PCPP_LOG_ERROR("Couldn't compile BPF filter: '" << m_FilterStr << "' for link type: " << linkType); + return false; + } + m_CachedProgram = std::move(newProgram); + m_CachedProgramLinkType = static_cast(linkType); + break; + } + default: + throw std::logic_error("Unknown LinkMissmatchBehaviour"); + } } - struct pcap_pkthdr pktHdr; + // Test the packet against the filter + pcap_pkthdr pktHdr; pktHdr.caplen = packetDataLength; pktHdr.len = packetDataLength; - pktHdr.ts = internal::toTimeval(packetTimestamp); + pktHdr.ts = internal::toTimeval(timestamp); + return (pcap_offline_filter(m_CachedProgram.get(), &pktHdr, packetData) != 0); + } + + BpfFilterWrapper::BpfProgramUPtr BpfFilterWrapper::compileFilter(std::string const& filter, LinkLayerType linkType) + { + if (filter.empty()) + return nullptr; + + auto pcap = std::unique_ptr(pcap_open_dead(linkType, DEFAULT_SNAPLEN)); + if (pcap == nullptr) + { + return nullptr; + } + + auto newProg = std::make_unique(); + int ret = pcap_compile(pcap.get(), newProg.get(), filter.c_str(), 1, 0); + if (ret < 0) + { + return nullptr; + } - return (pcap_offline_filter(m_Program.get(), &pktHdr, packetData) != 0); + // Reassigns ownership to a new unique_ptr with a custom deleter as it now requires specialized cleanup. + return BpfProgramUPtr(newProg.release()); } void BPFStringFilter::parseToString(std::string& result) From 9c34cefa43c2e093dd1f1f5a84e133b1b686a875 Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Wed, 10 Sep 2025 22:33:15 +0300 Subject: [PATCH 02/17] Fixed typo. --- Pcap++/header/PcapFilter.h | 20 ++++++++++---------- Pcap++/src/PcapFilter.cpp | 14 +++++++------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index 19ef221295..a85ca256d0 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -79,8 +79,8 @@ namespace pcpp class BpfFilterWrapper { public: - /// @brief An enum with possible behaviours in case of link type missmatch - enum class LinkMissmatchBehaviour + /// @brief An enum with possible behaviours in case of link type mismatch + enum class LinkMismatchBehaviour { /// @brief Attempt to recompile the filter with the new link type RecompileFilter, @@ -133,30 +133,30 @@ namespace pcpp /// /// If the filter is empty the method returns "true". /// If the link type of the raw packet is different than the one set in setFilter(): - /// - If onLinkMissmatch is set to RecompileFilter, the filter will be re-compiled and stored in the object. - /// - If onLinkMissmatch is set to NoMatch, the method will return "false" + /// - If onLinkmismatch is set to RecompileFilter, the filter will be re-compiled and stored in the object. + /// - If onLinkmismatch is set to NoMatch, the method will return "false" /// /// @param[in] rawPacket The raw packet to match the filter against - /// @param[in] onLinkMissmatch The behaviour in case of link type missmatch + /// @param[in] onLinkmismatch The behaviour in case of link type mismatch /// @return True if the filter matches (or if it's empty). False otherwise bool matches(const RawPacket& rawPacket, - LinkMissmatchBehaviour onLinkMissmatch = LinkMissmatchBehaviour::RecompileFilter) const; + LinkMismatchBehaviour onLinkmismatch = LinkMismatchBehaviour::RecompileFilter) const; /// @brief Match a raw buffer of packet data against the filter stored in this object. /// /// If the filter is empty the method returns "true". /// If the link type provided is different than the one set in setFilter(): - /// - If onLinkMissmatch is set to RecompileFilter, the filter will be re-compiled and stored in the object. - /// - If onLinkMissmatch is set to NoMatch, the method will return "false" + /// - If onLinkmismatch is set to RecompileFilter, the filter will be re-compiled and stored in the object. + /// - If onLinkmismatch is set to NoMatch, the method will return "false" /// /// @param[in] packetData A pointer to the raw packet data /// @param[in] packetDataLength The length of the raw packet data in bytes /// @param[in] timestamp Timestamp to be associated with the packet /// @param[in] linkType The link type of the packet - /// @param[in] onLinkMissmatch The behaviour in case of link type missmatch + /// @param[in] onLinkmismatch The behaviour in case of link type mismatch /// @return True if the filter matches (or if it's empty). False otherwise bool matches(const uint8_t* packetData, uint32_t packetDataLength, timespec timestamp, uint16_t linkType, - LinkMissmatchBehaviour onLinkMissmatch = LinkMissmatchBehaviour::RecompileFilter) const; + LinkMismatchBehaviour onLinkmismatch = LinkMismatchBehaviour::RecompileFilter) const; private: using BpfProgramUPtr = std::unique_ptr; diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index c5a0312f58..455915f860 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -101,14 +101,14 @@ namespace pcpp return matches(packetData, packetDataLength, packetTimestamp, linkType); } - bool BpfFilterWrapper::matches(const RawPacket& rawPacket, LinkMissmatchBehaviour onLinkMissmatch) const + bool BpfFilterWrapper::matches(const RawPacket& rawPacket, LinkMismatchBehaviour onLinkmismatch) const { return matches(rawPacket.getRawData(), rawPacket.getRawDataLen(), rawPacket.getPacketTimeStamp(), - rawPacket.getLinkLayerType(), onLinkMissmatch); + rawPacket.getLinkLayerType(), onLinkmismatch); } bool BpfFilterWrapper::matches(const uint8_t* packetData, uint32_t packetDataLength, timespec timestamp, - uint16_t linkType, LinkMissmatchBehaviour onLinkMissmatch) const + uint16_t linkType, LinkMismatchBehaviour onLinkmismatch) const { if (m_FilterStr.empty()) return true; @@ -122,13 +122,13 @@ namespace pcpp // Handle link type mismatch if (linkType != static_cast(m_CachedProgramLinkType)) { - switch (onLinkMissmatch) + switch (onLinkmismatch) { - case LinkMissmatchBehaviour::NoMatch: + case LinkMismatchBehaviour::NoMatch: { return false; // Do not attempt to recompile, just return false } - case LinkMissmatchBehaviour::RecompileFilter: + case LinkMismatchBehaviour::RecompileFilter: { auto newProgram = compileFilter(m_FilterStr, static_cast(linkType)); if (newProgram == nullptr) @@ -141,7 +141,7 @@ namespace pcpp break; } default: - throw std::logic_error("Unknown LinkMissmatchBehaviour"); + throw std::logic_error("Unknown LinkmismatchBehaviour"); } } From 44dc6e28a620f9cdf39a888d870491445447ed6f Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Thu, 11 Sep 2025 00:19:55 +0300 Subject: [PATCH 03/17] Fix explicit constructor. --- Pcap++/header/PcapFilter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index a85ca256d0..cdc1da2e0c 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -95,7 +95,7 @@ namespace pcpp /// @param filter A filter in BPF syntax /// @param linkType An optional parameter to set the filter's link type. The default is LINKTYPE_ETHERNET /// @throws std::runtime_error if the filter could not be compiled - BpfFilterWrapper(std::string filter, LinkLayerType linkType = LINKTYPE_ETHERNET); + explicit BpfFilterWrapper(std::string filter, LinkLayerType linkType = LINKTYPE_ETHERNET); BpfFilterWrapper(const BpfFilterWrapper& other); BpfFilterWrapper(BpfFilterWrapper&&) noexcept = default; From b0ce9c3e3b54ffad1f15bbf5cdff94e216e1aa20 Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Sun, 14 Sep 2025 22:17:25 +0300 Subject: [PATCH 04/17] Reverted parameters for configuring recompilation. --- Pcap++/header/PcapFilter.h | 25 ++++--------------------- Pcap++/src/PcapFilter.cpp | 31 +++++++++---------------------- 2 files changed, 13 insertions(+), 43 deletions(-) diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index cdc1da2e0c..229db314de 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -79,15 +79,6 @@ namespace pcpp class BpfFilterWrapper { public: - /// @brief An enum with possible behaviours in case of link type mismatch - enum class LinkMismatchBehaviour - { - /// @brief Attempt to recompile the filter with the new link type - RecompileFilter, - /// @brief Do not attempt to recompile the filter, just return false. - NoMatch - }; - /// @brief Creates a new instance with no filter. BpfFilterWrapper() = default; @@ -132,31 +123,23 @@ namespace pcpp /// @brief Match a packet with the filter stored in this object. /// /// If the filter is empty the method returns "true". - /// If the link type of the raw packet is different than the one set in setFilter(): - /// - If onLinkmismatch is set to RecompileFilter, the filter will be re-compiled and stored in the object. - /// - If onLinkmismatch is set to NoMatch, the method will return "false" + /// If the link type of the raw packet is different than the one set in setFilter() the filter will be recompiled. /// /// @param[in] rawPacket The raw packet to match the filter against - /// @param[in] onLinkmismatch The behaviour in case of link type mismatch /// @return True if the filter matches (or if it's empty). False otherwise - bool matches(const RawPacket& rawPacket, - LinkMismatchBehaviour onLinkmismatch = LinkMismatchBehaviour::RecompileFilter) const; + bool matches(const RawPacket& rawPacket) const; /// @brief Match a raw buffer of packet data against the filter stored in this object. /// /// If the filter is empty the method returns "true". - /// If the link type provided is different than the one set in setFilter(): - /// - If onLinkmismatch is set to RecompileFilter, the filter will be re-compiled and stored in the object. - /// - If onLinkmismatch is set to NoMatch, the method will return "false" + /// If the link type provided is different than the one set in setFilter() the filter will be recompiled. /// /// @param[in] packetData A pointer to the raw packet data /// @param[in] packetDataLength The length of the raw packet data in bytes /// @param[in] timestamp Timestamp to be associated with the packet /// @param[in] linkType The link type of the packet - /// @param[in] onLinkmismatch The behaviour in case of link type mismatch /// @return True if the filter matches (or if it's empty). False otherwise - bool matches(const uint8_t* packetData, uint32_t packetDataLength, timespec timestamp, uint16_t linkType, - LinkMismatchBehaviour onLinkmismatch = LinkMismatchBehaviour::RecompileFilter) const; + bool matches(const uint8_t* packetData, uint32_t packetDataLength, timespec timestamp, uint16_t linkType) const; private: using BpfProgramUPtr = std::unique_ptr; diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index 455915f860..37ebf5a05b 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -101,14 +101,14 @@ namespace pcpp return matches(packetData, packetDataLength, packetTimestamp, linkType); } - bool BpfFilterWrapper::matches(const RawPacket& rawPacket, LinkMismatchBehaviour onLinkmismatch) const + bool BpfFilterWrapper::matches(const RawPacket& rawPacket) const { return matches(rawPacket.getRawData(), rawPacket.getRawDataLen(), rawPacket.getPacketTimeStamp(), - rawPacket.getLinkLayerType(), onLinkmismatch); + rawPacket.getLinkLayerType()); } bool BpfFilterWrapper::matches(const uint8_t* packetData, uint32_t packetDataLength, timespec timestamp, - uint16_t linkType, LinkMismatchBehaviour onLinkmismatch) const + uint16_t linkType) const { if (m_FilterStr.empty()) return true; @@ -122,27 +122,14 @@ namespace pcpp // Handle link type mismatch if (linkType != static_cast(m_CachedProgramLinkType)) { - switch (onLinkmismatch) - { - case LinkMismatchBehaviour::NoMatch: - { - return false; // Do not attempt to recompile, just return false - } - case LinkMismatchBehaviour::RecompileFilter: + auto newProgram = compileFilter(m_FilterStr, static_cast(linkType)); + if (newProgram == nullptr) { - auto newProgram = compileFilter(m_FilterStr, static_cast(linkType)); - if (newProgram == nullptr) - { - PCPP_LOG_ERROR("Couldn't compile BPF filter: '" << m_FilterStr << "' for link type: " << linkType); - return false; - } - m_CachedProgram = std::move(newProgram); - m_CachedProgramLinkType = static_cast(linkType); - break; - } - default: - throw std::logic_error("Unknown LinkmismatchBehaviour"); + PCPP_LOG_ERROR("Couldn't compile BPF filter: '" << m_FilterStr << "' for link type: " << linkType); + return false; } + m_CachedProgram = std::move(newProgram); + m_CachedProgramLinkType = static_cast(linkType); } // Test the packet against the filter From 9b5fc6380a41a23870db2a2e17aed1442ee8a2f5 Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Sun, 14 Sep 2025 23:02:59 +0300 Subject: [PATCH 05/17] Extended general filter to skip reparsing on every match request. --- Pcap++/header/PcapFilter.h | 102 +++++++++++++++++++++++++++---------- Pcap++/src/PcapFilter.cpp | 84 +++++++++++++++++++----------- 2 files changed, 130 insertions(+), 56 deletions(-) diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index 229db314de..6ef79b8408 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -123,7 +123,8 @@ namespace pcpp /// @brief Match a packet with the filter stored in this object. /// /// If the filter is empty the method returns "true". - /// If the link type of the raw packet is different than the one set in setFilter() the filter will be recompiled. + /// If the link type of the raw packet is different than the one set in setFilter() the filter will be + /// recompiled. /// /// @param[in] rawPacket The raw packet to match the filter against /// @return True if the filter matches (or if it's empty). False otherwise @@ -157,25 +158,47 @@ namespace pcpp /// For deeper understanding of the filter concept please refer to PcapFilter.h class GeneralFilter { - protected: - BpfFilterWrapper m_BpfWrapper; - public: + GeneralFilter() = default; + + /// Virtual destructor, frees the bpf program + virtual ~GeneralFilter() = default; + /// A method that parses the class instance into BPF string format /// @param[out] result An empty string that the parsing will be written into. If the string isn't empty, its /// content will be overridden - virtual void parseToString(std::string& result) = 0; + virtual void parseToString(std::string& result) const = 0; /// Match a raw packet with a given BPF filter. /// @param[in] rawPacket A pointer to the raw packet to match the BPF filter with /// @return True if a raw packet matches the BPF filter or false otherwise bool matchPacketWithFilter(RawPacket* rawPacket); - GeneralFilter() - {} + /// @brief Match a raw packet against the filter. + /// @param rawPacket The raw packet to match against. + /// @return True if the filter matches (or if it's empty). False otherwise. + bool matches(RawPacket const& rawPacket) const; - /// Virtual destructor, frees the bpf program - virtual ~GeneralFilter() = default; + protected: + /// @brief Parse the filter and cache the compiled BPF program. + /// @return True if the filter was successfully parsed and cached, false otherwise. + bool cacheFilter() + { + return cacheFilterInternal(); + } + + /// @brief Invalidate the cached BPF program. This method should be called whenever the filter changes. + void invalidateCache() + { + m_CachedFilter = false; + }; + + private: + // This method does the actual caching. It is const so it can be called from const methods. + bool cacheFilterInternal() const; + + mutable BpfFilterWrapper m_BpfWrapper; + mutable bool m_CachedFilter = false; }; /// @class BPFStringFilter @@ -195,7 +218,7 @@ namespace pcpp /// A method that parses the class instance into BPF string format /// @param[out] result An empty string that the parsing will be written into. If the string isn't empty, its /// content will be overridden If the filter is not valid the result will be an empty string - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Verify the filter is valid /// @return True if the filter is valid or false otherwise @@ -213,7 +236,7 @@ namespace pcpp Direction m_Dir; protected: - void parseDirection(std::string& directionAsString); + void parseDirection(std::string& directionAsString) const; Direction getDir() const { return m_Dir; @@ -243,7 +266,7 @@ namespace pcpp FilterOperator m_Operator; protected: - std::string parseOperator(); + std::string parseOperator() const; FilterOperator getOperator() const { return m_Operator; @@ -259,6 +282,7 @@ namespace pcpp void setOperator(FilterOperator op) { m_Operator = op; + invalidateCache(); } }; @@ -349,7 +373,7 @@ namespace pcpp : IFilterWithDirection(dir), m_Address(network.getNetworkPrefix()), m_Network(network) {} - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Set the network to build the filter with. /// @param[in] network The IP Network object to be used when building the filter. @@ -357,6 +381,7 @@ namespace pcpp { m_Network = network; m_Address = m_Network.getNetworkPrefix(); + invalidateCache(); } /// Set the IP address @@ -365,6 +390,7 @@ namespace pcpp void setAddr(const std::string& ipAddress) { this->setAddr(IPAddress(ipAddress)); + invalidateCache(); } /// Set the IP address @@ -382,6 +408,7 @@ namespace pcpp } m_Network = IPNetwork(m_Address, newPrefixLen); + invalidateCache(); } /// Set the subnet mask @@ -395,12 +422,14 @@ namespace pcpp void setMask(const std::string& netmask) { m_Network = IPNetwork(m_Address, netmask); + invalidateCache(); } /// Clears the subnet mask. void clearMask() { this->clearLen(); + invalidateCache(); } /// Set the subnet (IPv4) or prefix length (IPv6). @@ -410,12 +439,14 @@ namespace pcpp void setLen(const int len) { m_Network = IPNetwork(m_Address, len); + invalidateCache(); } /// Clears the subnet mask length. void clearLen() { m_Network = IPNetwork(m_Address); + invalidateCache(); } }; @@ -436,13 +467,14 @@ namespace pcpp IPv4IDFilter(uint16_t ipID, FilterOperator op) : IFilterWithOperator(op), m_IpID(ipID) {} - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Set the IP ID to filter /// @param[in] ipID The IP ID to filter void setIpID(uint16_t ipID) { m_IpID = ipID; + invalidateCache(); } }; @@ -464,13 +496,14 @@ namespace pcpp : IFilterWithOperator(op), m_TotalLength(totalLength) {} - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Set the total length value /// @param[in] totalLength The total length value to filter void setTotalLength(uint16_t totalLength) { m_TotalLength = totalLength; + invalidateCache(); } }; @@ -490,13 +523,14 @@ namespace pcpp /// @param[in] dir The port direction to filter (source or destination) PortFilter(uint16_t port, Direction dir); - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Set the port /// @param[in] port The port to create the filter with void setPort(uint16_t port) { portToString(port); + invalidateCache(); } }; @@ -521,13 +555,14 @@ namespace pcpp : IFilterWithDirection(dir), m_FromPort(fromPort), m_ToPort(toPort) {} - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Set the lower end of the port range /// @param[in] fromPort The lower end of the port range void setFromPort(uint16_t fromPort) { m_FromPort = fromPort; + invalidateCache(); } /// Set the higher end of the port range @@ -535,6 +570,7 @@ namespace pcpp void setToPort(uint16_t toPort) { m_ToPort = toPort; + invalidateCache(); } }; @@ -555,13 +591,14 @@ namespace pcpp MacAddressFilter(MacAddress address, Direction dir) : IFilterWithDirection(dir), m_MacAddress(address) {} - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Set the MAC address /// @param[in] address The MAC address to use for filtering void setMacAddress(MacAddress address) { m_MacAddress = address; + invalidateCache(); } }; @@ -581,13 +618,14 @@ namespace pcpp explicit EtherTypeFilter(uint16_t etherType) : m_EtherType(etherType) {} - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Set the EtherType value /// @param[in] etherType The EtherType value to create the filter with void setEtherType(uint16_t etherType) { m_EtherType = etherType; + invalidateCache(); } }; @@ -614,6 +652,7 @@ namespace pcpp void addFilter(GeneralFilter* filter) { m_FilterList.push_back(filter); + invalidateCache(); } /// Removes the first matching filter from the composite filter @@ -628,6 +667,7 @@ namespace pcpp void clearAllFilters() { m_FilterList.clear(); + invalidateCache(); } }; @@ -667,7 +707,7 @@ namespace pcpp public: using CompositeFilter::CompositeFilter; - void parseToString(std::string& result) override + void parseToString(std::string& result) const override { result.clear(); for (auto it = m_FilterList.cbegin(); it != m_FilterList.cend(); ++it) @@ -718,13 +758,14 @@ namespace pcpp m_FilterToInverse = filterToInverse; } - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Set a filter to create an inverse filter from /// @param[in] filterToInverse A pointer to filter which the created filter be the inverse of void setFilter(GeneralFilter* filterToInverse) { m_FilterToInverse = filterToInverse; + invalidateCache(); } }; @@ -755,7 +796,7 @@ namespace pcpp explicit ProtoFilter(ProtocolTypeFamily protoFamily) : m_ProtoFamily(protoFamily) {} - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Set the protocol to filter with /// @param[in] proto The protocol to filter, only packets matching this protocol will be received. Please note @@ -763,6 +804,7 @@ namespace pcpp void setProto(ProtocolType proto) { m_ProtoFamily = proto; + invalidateCache(); } /// Set the protocol family to filter with @@ -772,6 +814,7 @@ namespace pcpp void setProto(ProtocolTypeFamily protoFamily) { m_ProtoFamily = protoFamily; + invalidateCache(); } }; @@ -791,13 +834,14 @@ namespace pcpp explicit ArpFilter(ArpOpcode opCode) : m_OpCode(opCode) {} - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Set the ARP opcode /// @param[in] opCode The ARP opcode: ::ARP_REQUEST or ::ARP_REPLY void setOpCode(ArpOpcode opCode) { m_OpCode = opCode; + invalidateCache(); } }; @@ -817,13 +861,14 @@ namespace pcpp explicit VlanFilter(uint16_t vlanId) : m_VlanID(vlanId) {} - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Set the VLAN ID of the filter /// @param[in] vlanId The VLAN ID to use for the filter void setVlanID(uint16_t vlanId) { m_VlanID = vlanId; + invalidateCache(); } }; @@ -885,9 +930,10 @@ namespace pcpp { m_TcpFlagsBitMask = tcpFlagBitMask; m_MatchOption = matchOption; + invalidateCache(); } - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; }; /// @class TcpWindowSizeFilter @@ -907,13 +953,14 @@ namespace pcpp TcpWindowSizeFilter(uint16_t windowSize, FilterOperator op) : IFilterWithOperator(op), m_WindowSize(windowSize) {} - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Set window-size value /// @param[in] windowSize The window-size value that will be used in the filter void setWindowSize(uint16_t windowSize) { m_WindowSize = windowSize; + invalidateCache(); } }; @@ -934,13 +981,14 @@ namespace pcpp UdpLengthFilter(uint16_t length, FilterOperator op) : IFilterWithOperator(op), m_Length(length) {} - void parseToString(std::string& result) override; + void parseToString(std::string& result) const override; /// Set length value /// @param[in] length The length value that will be used in the filter void setLength(uint16_t length) { m_Length = length; + invalidateCache(); } }; } // namespace pcpp diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index 37ebf5a05b..130874771d 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -18,17 +18,6 @@ namespace pcpp static const int DEFAULT_SNAPLEN = 9000; - bool GeneralFilter::matchPacketWithFilter(RawPacket* rawPacket) - { - std::string filterStr; - parseToString(filterStr); - - if (!m_BpfWrapper.setFilter(filterStr)) - return false; - - return m_BpfWrapper.matchPacketWithFilter(rawPacket); - } - namespace internal { void BpfProgramDeleter::operator()(bpf_program* ptr) const noexcept @@ -162,17 +151,52 @@ namespace pcpp return BpfProgramUPtr(newProg.release()); } - void BPFStringFilter::parseToString(std::string& result) + bool GeneralFilter::matchPacketWithFilter(RawPacket* rawPacket) + { + if (rawPacket == nullptr) + { + PCPP_LOG_ERROR("Raw packet pointer is null"); + return false; + } + + return matches(*rawPacket); + } + + bool GeneralFilter::matches(RawPacket const& rawPacket) const + { + if (!m_CachedFilter) + { + if (!cacheFilterInternal()) + { + return false; + } + } + + return m_BpfWrapper.matches(rawPacket); + } + + bool GeneralFilter::cacheFilterInternal() const + { + std::string filterStr; + parseToString(filterStr); + if (!m_BpfWrapper.setFilter(filterStr)) + return false; + + m_CachedFilter = true; + return true; + } + + void BPFStringFilter::parseToString(std::string& result) const { result = m_FilterStr; } bool BPFStringFilter::verifyFilter() { - return m_BpfWrapper.setFilter(m_FilterStr); + return cacheFilter(); } - void IFilterWithDirection::parseDirection(std::string& directionAsString) + void IFilterWithDirection::parseDirection(std::string& directionAsString) const { switch (m_Dir) { @@ -188,7 +212,7 @@ namespace pcpp } } - std::string IFilterWithOperator::parseOperator() + std::string IFilterWithOperator::parseOperator() const { switch (m_Operator) { @@ -209,7 +233,7 @@ namespace pcpp } } - void IPFilter::parseToString(std::string& result) + void IPFilter::parseToString(std::string& result) const { std::string dir; std::string ipAddr = m_Network.toString(); @@ -225,7 +249,7 @@ namespace pcpp result += ipAddr; } - void IPv4IDFilter::parseToString(std::string& result) + void IPv4IDFilter::parseToString(std::string& result) const { std::string op = parseOperator(); std::ostringstream stream; @@ -233,7 +257,7 @@ namespace pcpp result = "ip[4:2] " + op + ' ' + stream.str(); } - void IPv4TotalLengthFilter::parseToString(std::string& result) + void IPv4TotalLengthFilter::parseToString(std::string& result) const { std::string op = parseOperator(); std::ostringstream stream; @@ -253,14 +277,14 @@ namespace pcpp portToString(port); } - void PortFilter::parseToString(std::string& result) + void PortFilter::parseToString(std::string& result) const { std::string dir; parseDirection(dir); result = dir + " port " + m_Port; } - void PortRangeFilter::parseToString(std::string& result) + void PortRangeFilter::parseToString(std::string& result) const { std::string dir; parseDirection(dir); @@ -273,7 +297,7 @@ namespace pcpp result = dir + " portrange " + fromPortStream.str() + '-' + toPortStream.str(); } - void MacAddressFilter::parseToString(std::string& result) + void MacAddressFilter::parseToString(std::string& result) const { if (getDir() != SRC_OR_DST) { @@ -285,7 +309,7 @@ namespace pcpp result = "ether host " + m_MacAddress.toString(); } - void EtherTypeFilter::parseToString(std::string& result) + void EtherTypeFilter::parseToString(std::string& result) const { std::ostringstream stream; stream << "0x" << std::hex << m_EtherType; @@ -302,6 +326,7 @@ namespace pcpp if (*it == filter) { m_FilterList.erase(it); + invalidateCache(); break; } } @@ -310,16 +335,17 @@ namespace pcpp void CompositeFilter::setFilters(const std::vector& filters) { m_FilterList = filters; + invalidateCache(); } - void NotFilter::parseToString(std::string& result) + void NotFilter::parseToString(std::string& result) const { std::string innerFilterAsString; m_FilterToInverse->parseToString(innerFilterAsString); result = "not (" + innerFilterAsString + ')'; } - void ProtoFilter::parseToString(std::string& result) + void ProtoFilter::parseToString(std::string& result) const { std::ostringstream stream; @@ -362,21 +388,21 @@ namespace pcpp } } - void ArpFilter::parseToString(std::string& result) + void ArpFilter::parseToString(std::string& result) const { std::ostringstream sstream; sstream << "arp[7] = " << m_OpCode; result += sstream.str(); } - void VlanFilter::parseToString(std::string& result) + void VlanFilter::parseToString(std::string& result) const { std::ostringstream stream; stream << m_VlanID; result = "vlan " + stream.str(); } - void TcpFlagsFilter::parseToString(std::string& result) + void TcpFlagsFilter::parseToString(std::string& result) const { if (m_TcpFlagsBitMask == 0) { @@ -411,14 +437,14 @@ namespace pcpp } } - void TcpWindowSizeFilter::parseToString(std::string& result) + void TcpWindowSizeFilter::parseToString(std::string& result) const { std::ostringstream stream; stream << m_WindowSize; result = "tcp[14:2] " + parseOperator() + ' ' + stream.str(); } - void UdpLengthFilter::parseToString(std::string& result) + void UdpLengthFilter::parseToString(std::string& result) const { std::ostringstream stream; stream << m_Length; From e69bdd39539fb0d7f0fc709b131115e76043b30d Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Mon, 15 Sep 2025 14:27:35 +0300 Subject: [PATCH 06/17] Revert reorder. --- Pcap++/header/PcapFilter.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index 6ef79b8408..f9bc767167 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -78,6 +78,15 @@ namespace pcpp /// A wrapper class for BPF filtering. Enables setting a BPF filter and matching it against a packet class BpfFilterWrapper { + private: + using BpfProgramUPtr = std::unique_ptr; + + std::string m_FilterStr; + mutable LinkLayerType m_CachedProgramLinkType = LinkLayerType::LINKTYPE_ETHERNET; + mutable BpfProgramUPtr m_CachedProgram; + + static BpfProgramUPtr compileFilter(std::string const& filter, LinkLayerType linkType); + public: /// @brief Creates a new instance with no filter. BpfFilterWrapper() = default; @@ -141,15 +150,6 @@ namespace pcpp /// @param[in] linkType The link type of the packet /// @return True if the filter matches (or if it's empty). False otherwise bool matches(const uint8_t* packetData, uint32_t packetDataLength, timespec timestamp, uint16_t linkType) const; - - private: - using BpfProgramUPtr = std::unique_ptr; - - std::string m_FilterStr; - mutable LinkLayerType m_CachedProgramLinkType = LinkLayerType::LINKTYPE_ETHERNET; - mutable BpfProgramUPtr m_CachedProgram; - - static BpfProgramUPtr compileFilter(std::string const& filter, LinkLayerType linkType); }; /// @class GeneralFilter From b67579c5e498e1e4f5fef1fc7d8248d58ae747ee Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Mon, 15 Sep 2025 14:29:24 +0300 Subject: [PATCH 07/17] Revert reorder. --- Pcap++/header/PcapFilter.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index f9bc767167..b14fbb599c 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -158,6 +158,10 @@ namespace pcpp /// For deeper understanding of the filter concept please refer to PcapFilter.h class GeneralFilter { + private: + mutable BpfFilterWrapper m_BpfWrapper; + mutable bool m_CachedFilter = false; + public: GeneralFilter() = default; @@ -196,9 +200,6 @@ namespace pcpp private: // This method does the actual caching. It is const so it can be called from const methods. bool cacheFilterInternal() const; - - mutable BpfFilterWrapper m_BpfWrapper; - mutable bool m_CachedFilter = false; }; /// @class BPFStringFilter From b06fcef1ba14c29584c61ab063b2c13df2f293df Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Sat, 20 Sep 2025 12:35:12 +0300 Subject: [PATCH 08/17] Fixed missed cache invalidation. --- Pcap++/header/PcapFilter.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index b14fbb599c..fe6afa4ee3 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -253,6 +253,7 @@ namespace pcpp void setDirection(Direction dir) { m_Dir = dir; + invalidateCache(); } }; From fc45b30f9574f582e6365eac922dd7a81e37f4a6 Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Sat, 20 Sep 2025 12:43:08 +0300 Subject: [PATCH 09/17] Merged cacheFilter and cacheFilter internal. --- Pcap++/header/PcapFilter.h | 11 ++--------- Pcap++/src/PcapFilter.cpp | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index fe6afa4ee3..5eb18e3e4d 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -186,20 +186,13 @@ namespace pcpp protected: /// @brief Parse the filter and cache the compiled BPF program. /// @return True if the filter was successfully parsed and cached, false otherwise. - bool cacheFilter() - { - return cacheFilterInternal(); - } + bool cacheFilter() const; /// @brief Invalidate the cached BPF program. This method should be called whenever the filter changes. - void invalidateCache() + void invalidateCache() const { m_CachedFilter = false; }; - - private: - // This method does the actual caching. It is const so it can be called from const methods. - bool cacheFilterInternal() const; }; /// @class BPFStringFilter diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index 130874771d..6e2edc45a8 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -175,7 +175,7 @@ namespace pcpp return m_BpfWrapper.matches(rawPacket); } - bool GeneralFilter::cacheFilterInternal() const + bool GeneralFilter::cacheFilter() const { std::string filterStr; parseToString(filterStr); From 4e801218a2116db1fa21d96d86aace4b1ac5cda8 Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Sat, 20 Sep 2025 12:46:25 +0300 Subject: [PATCH 10/17] Fix function name. --- Pcap++/src/PcapFilter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index 6e2edc45a8..7bd5a86843 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -166,7 +166,7 @@ namespace pcpp { if (!m_CachedFilter) { - if (!cacheFilterInternal()) + if (!cacheFilter()) { return false; } From f58ce68adf25fe1218d02d8dacab1c7dea73d89a Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Sat, 20 Sep 2025 12:56:24 +0300 Subject: [PATCH 11/17] Added const to old matchPaketWithFilter. --- Pcap++/header/PcapFilter.h | 7 ++++--- Pcap++/src/PcapFilter.cpp | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index 5eb18e3e4d..94afc40a97 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -7,6 +7,7 @@ #include #include "ArpLayer.h" #include "RawPacket.h" +#include "DeprecationUtils.h" // Forward Declaration - used in GeneralFilter struct bpf_program; @@ -115,7 +116,7 @@ namespace pcpp /// @param[in] rawPacket A pointer to a raw packet which the filter will be matched against /// @return True if the filter matches (or if it's empty). False if the packet doesn't match or if the filter /// could not be compiled - bool matchPacketWithFilter(const RawPacket* rawPacket); + bool matchPacketWithFilter(const RawPacket* rawPacket) const; /// Match a packet data with the filter stored in this object. If the filter is empty the method returns "true". /// If the link type provided is different than the one set in setFilter(), the filter will be re-compiled @@ -127,7 +128,7 @@ namespace pcpp /// @return True if the filter matches (or if it's empty). False if the packet doesn't match or if the filter /// could not be compiled bool matchPacketWithFilter(const uint8_t* packetData, uint32_t packetDataLength, timespec packetTimestamp, - uint16_t linkType); + uint16_t linkType) const; /// @brief Match a packet with the filter stored in this object. /// @@ -176,7 +177,7 @@ namespace pcpp /// Match a raw packet with a given BPF filter. /// @param[in] rawPacket A pointer to the raw packet to match the BPF filter with /// @return True if a raw packet matches the BPF filter or false otherwise - bool matchPacketWithFilter(RawPacket* rawPacket); + bool matchPacketWithFilter(RawPacket* rawPacket) const; /// @brief Match a raw packet against the filter. /// @param rawPacket The raw packet to match against. diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index 7bd5a86843..99e5898622 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -73,7 +73,7 @@ namespace pcpp return true; } - bool BpfFilterWrapper::matchPacketWithFilter(const RawPacket* rawPacket) + bool BpfFilterWrapper::matchPacketWithFilter(const RawPacket* rawPacket) const { if (rawPacket == nullptr) { @@ -85,7 +85,7 @@ namespace pcpp } bool BpfFilterWrapper::matchPacketWithFilter(const uint8_t* packetData, uint32_t packetDataLength, - timespec packetTimestamp, uint16_t linkType) + timespec packetTimestamp, uint16_t linkType) const { return matches(packetData, packetDataLength, packetTimestamp, linkType); } @@ -151,7 +151,7 @@ namespace pcpp return BpfProgramUPtr(newProg.release()); } - bool GeneralFilter::matchPacketWithFilter(RawPacket* rawPacket) + bool GeneralFilter::matchPacketWithFilter(RawPacket* rawPacket) const { if (rawPacket == nullptr) { From e6d7d21a126d589bc9079dbe6ec869d4e7632657 Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Sat, 20 Sep 2025 12:59:06 +0300 Subject: [PATCH 12/17] Remove unused include. --- Pcap++/header/PcapFilter.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index 94afc40a97..f1a6cbbc2f 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -7,7 +7,6 @@ #include #include "ArpLayer.h" #include "RawPacket.h" -#include "DeprecationUtils.h" // Forward Declaration - used in GeneralFilter struct bpf_program; From 7efcc7be9f7d0368c2163b156f38ee6ce8d18c8d Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Thu, 25 Sep 2025 12:33:37 +0300 Subject: [PATCH 13/17] Attempt a recompile if no program has been compiled yet. --- Pcap++/src/PcapFilter.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index 99e5898622..4cadda7792 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -102,14 +102,8 @@ namespace pcpp if (m_FilterStr.empty()) return true; - // This should never happen, but just in case - if (m_CachedProgram == nullptr) - { - throw std::runtime_error("No compiled BPF program available"); - } - - // Handle link type mismatch - if (linkType != static_cast(m_CachedProgramLinkType)) + // Handle uncompiled program or link type mismatch + if (m_CachedProgram == nullptr || linkType != static_cast(m_CachedProgramLinkType)) { auto newProgram = compileFilter(m_FilterStr, static_cast(linkType)); if (newProgram == nullptr) From 3c17eb77e10abf0b59fcfb42480cdce82340aeb6 Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Thu, 25 Sep 2025 12:34:30 +0300 Subject: [PATCH 14/17] Simplfy nested if. --- Pcap++/src/PcapFilter.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index 4cadda7792..490d19c322 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -158,12 +158,9 @@ namespace pcpp bool GeneralFilter::matches(RawPacket const& rawPacket) const { - if (!m_CachedFilter) + if (!m_CachedFilter && !cacheFilter()) { - if (!cacheFilter()) - { - return false; - } + return false; } return m_BpfWrapper.matches(rawPacket); From fd6165a3d4d6419f20c056ae775eb9cf493cf5eb Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Thu, 25 Sep 2025 12:34:59 +0300 Subject: [PATCH 15/17] Add braces to if. --- Pcap++/src/PcapFilter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index 490d19c322..710395b4df 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -171,7 +171,9 @@ namespace pcpp std::string filterStr; parseToString(filterStr); if (!m_BpfWrapper.setFilter(filterStr)) + { return false; + } m_CachedFilter = true; return true; From 7705ff2c6943f56005bb822f726cb6a37c4e681d Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Wed, 1 Oct 2025 12:02:36 +0300 Subject: [PATCH 16/17] Removed constructor from BpfFilterWrapper. --- Pcap++/header/PcapFilter.h | 6 ------ Pcap++/src/PcapFilter.cpp | 17 +++++++---------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index f1a6cbbc2f..caea80608c 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -91,12 +91,6 @@ namespace pcpp /// @brief Creates a new instance with no filter. BpfFilterWrapper() = default; - /// @brief Creates a new instance with the given filter string. - /// @param filter A filter in BPF syntax - /// @param linkType An optional parameter to set the filter's link type. The default is LINKTYPE_ETHERNET - /// @throws std::runtime_error if the filter could not be compiled - explicit BpfFilterWrapper(std::string filter, LinkLayerType linkType = LINKTYPE_ETHERNET); - BpfFilterWrapper(const BpfFilterWrapper& other); BpfFilterWrapper(BpfFilterWrapper&&) noexcept = default; BpfFilterWrapper& operator=(const BpfFilterWrapper& other); diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index 710395b4df..60b397a740 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -27,23 +27,20 @@ namespace pcpp } } // namespace internal - BpfFilterWrapper::BpfFilterWrapper(std::string filter, LinkLayerType linkType) - : m_FilterStr(std::move(filter)), m_CachedProgramLinkType(linkType), - m_CachedProgram(compileFilter(m_FilterStr, linkType)) + BpfFilterWrapper::BpfFilterWrapper(const BpfFilterWrapper& other) { - if (!m_FilterStr.empty() && m_CachedProgram == nullptr) + if (!setFilter(other.m_FilterStr, other.m_CachedProgramLinkType)) { - throw std::runtime_error("Couldn't compile BPF filter: '" + m_FilterStr + "'"); + throw std::runtime_error("Couldn't compile BPF filter: '" + other.m_FilterStr + "'"); } } - BpfFilterWrapper::BpfFilterWrapper(const BpfFilterWrapper& other) - : BpfFilterWrapper(other.m_FilterStr, other.m_CachedProgramLinkType) - {} - BpfFilterWrapper& BpfFilterWrapper::operator=(const BpfFilterWrapper& other) { - setFilter(other.m_FilterStr, other.m_CachedProgramLinkType); + if (!setFilter(other.m_FilterStr, other.m_CachedProgramLinkType)) + { + throw std::runtime_error("Couldn't compile BPF filter: '" + other.m_FilterStr + "'"); + } return *this; } From f9774b462fae72d47b8210dcb5b639794f80f107 Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Wed, 1 Oct 2025 12:04:10 +0300 Subject: [PATCH 17/17] Suppressed some spammed error messages in tests. --- Tests/Pcap++Test/Tests/FilterTests.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tests/Pcap++Test/Tests/FilterTests.cpp b/Tests/Pcap++Test/Tests/FilterTests.cpp index 1636458f3a..5a28409e17 100644 --- a/Tests/Pcap++Test/Tests/FilterTests.cpp +++ b/Tests/Pcap++Test/Tests/FilterTests.cpp @@ -10,6 +10,7 @@ #include "Packet.h" #include "PcapLiveDeviceList.h" #include "PcapFileDevice.h" +#include "Logger.h" #include "../Common/GlobalTestArgs.h" #include "../Common/PcapFileNamesDef.h" #include "../Common/TestUtils.h" @@ -208,7 +209,9 @@ PTF_TEST_CASE(TestPcapFilters_General_BPFStr) // Try to make an invalid filter pcpp::BPFStringFilter badFilter("This is not a valid filter"); + pcpp::Logger::getInstance().suppressLogs(); PTF_ASSERT_FALSE(badFilter.verifyFilter()); + pcpp::Logger::getInstance().enableLogs(); // Test stolen from MacAddress test below pcpp::MacAddress macAddr("00:13:c3:df:ae:18"); @@ -252,7 +255,9 @@ PTF_TEST_CASE(TestPcapFilters_MatchStatic) pcpp::BPFStringFilter emptyFilter(""); PTF_ASSERT_TRUE(emptyFilter.matchPacketWithFilter(*iter)); pcpp::BPFStringFilter wrongFilter("-"); + pcpp::Logger::getInstance().suppressLogs(); PTF_ASSERT_FALSE(wrongFilter.matchPacketWithFilter(*iter)); + pcpp::Logger::getInstance().enableLogs(); } rawPacketVec.clear();