From 5a6de4e00e9ed899c544cbf32f50bedadac7d9b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20P=C3=A9ron?= Date: Tue, 7 Feb 2023 09:40:50 +0100 Subject: [PATCH] all: dos2unix (#1065) * Pcap++: convert dos2unix * Common++: convert dos2unix * Packet++: convert dos2unix * pre-commit: enable LF EOL checking * README: convert dos2unix * Add .gitattributes to normalizes line endings to LF for all text files * Revert "pre-commit: enable LF EOL checking" This reverts commit 457a4201421a422fb4d0b7474902d99fc13f909e. --- .gitattributes | 1 + Common++/header/IpUtils.h | 182 +- Common++/header/Logger.h | 500 ++--- Common++/header/PcapPlusPlusVersion.h | 108 +- Common++/header/SystemUtils.h | 792 +++---- Common++/src/IpUtils.cpp | 786 +++---- Common++/src/SystemUtils.cpp | 772 +++---- Packet++/header/DhcpLayer.h | 1642 +++++++------- Packet++/header/HttpLayer.h | 1406 ++++++------ Packet++/header/IcmpV6Layer.h | 564 ++--- Packet++/header/IgmpLayer.h | 1028 ++++----- Packet++/header/NdpLayer.h | 742 +++---- Packet++/header/NullLoopbackLayer.h | 184 +- Packet++/header/Packet.h | 784 +++---- Packet++/header/PacketTrailerLayer.h | 168 +- Packet++/header/PacketUtils.h | 152 +- Packet++/header/ProtocolType.h | 640 +++--- Packet++/header/RawPacket.h | 910 ++++---- Packet++/header/SSLLayer.h | 1116 +++++----- Packet++/header/SdpLayer.h | 338 +-- Packet++/header/SipLayer.h | 1354 ++++++------ Packet++/header/SllLayer.h | 220 +- Packet++/header/SomeIpLayer.h | 922 ++++---- Packet++/header/SomeIpSdLayer.h | 1538 ++++++------- Packet++/header/TcpLayer.h | 1012 ++++----- Packet++/header/TcpReassembly.h | 920 ++++---- Packet++/src/DhcpLayer.cpp | 628 +++--- Packet++/src/DnsLayer.cpp | 1756 +++++++-------- Packet++/src/GreLayer.cpp | 1236 +++++------ Packet++/src/HttpLayer.cpp | 2694 +++++++++++------------ Packet++/src/IPv4Layer.cpp | 1160 +++++----- Packet++/src/IcmpLayer.cpp | 1398 ++++++------ Packet++/src/IcmpV6Layer.cpp | 338 +-- Packet++/src/IgmpLayer.cpp | 1108 +++++----- Packet++/src/Layer.cpp | 210 +- Packet++/src/NdpLayer.cpp | 438 ++-- Packet++/src/NullLoopbackLayer.cpp | 214 +- Packet++/src/Packet.cpp | 1606 +++++++------- Packet++/src/PacketTrailerLayer.cpp | 54 +- Packet++/src/PacketUtils.cpp | 404 ++-- Packet++/src/PayloadLayer.cpp | 36 +- Packet++/src/RadiusLayer.cpp | 516 ++--- Packet++/src/RawPacket.cpp | 614 +++--- Packet++/src/SSLLayer.cpp | 516 ++--- Packet++/src/SdpLayer.cpp | 272 +-- Packet++/src/SipLayer.cpp | 2582 +++++++++++----------- Packet++/src/SomeIpLayer.cpp | 732 +++---- Packet++/src/SomeIpSdLayer.cpp | 1578 +++++++------- Packet++/src/TcpLayer.cpp | 866 ++++---- Packet++/src/TcpReassembly.cpp | 1544 ++++++------- Packet++/src/UdpLayer.cpp | 326 +-- Pcap++/header/DpdkDevice.h | 1658 +++++++------- Pcap++/header/DpdkDeviceList.h | 410 ++-- Pcap++/header/NetworkUtils.h | 180 +- Pcap++/header/PcapDevice.h | 200 +- Pcap++/header/PcapFileDevice.h | 1190 +++++----- Pcap++/header/PcapFilter.h | 1606 +++++++------- Pcap++/header/PcapLiveDeviceList.h | 242 +-- Pcap++/header/PcapRemoteDevice.h | 294 +-- Pcap++/header/PcapRemoteDeviceList.h | 304 +-- Pcap++/header/PfRingDevice.h | 698 +++--- Pcap++/header/PfRingDeviceList.h | 134 +- Pcap++/header/RawSocketDevice.h | 328 +-- Pcap++/header/WinPcapLiveDevice.h | 128 +- Pcap++/src/DpdkDevice.cpp | 2880 ++++++++++++------------- Pcap++/src/DpdkDeviceList.cpp | 848 ++++---- Pcap++/src/NetworkUtils.cpp | 894 ++++---- Pcap++/src/PcapDevice.cpp | 140 +- Pcap++/src/PcapFileDevice.cpp | 1838 ++++++++-------- Pcap++/src/PcapFilter.cpp | 926 ++++---- Pcap++/src/PcapLiveDevice.cpp | 1960 ++++++++--------- Pcap++/src/PcapLiveDeviceList.cpp | 718 +++--- Pcap++/src/PcapRemoteDevice.cpp | 280 +-- Pcap++/src/PcapRemoteDeviceList.cpp | 446 ++-- Pcap++/src/PfRingDevice.cpp | 1774 +++++++-------- Pcap++/src/PfRingDeviceList.cpp | 202 +- Pcap++/src/RawSocketDevice.cpp | 1144 +++++----- Pcap++/src/WinPcapLiveDevice.cpp | 262 +-- README.md | 592 ++--- 79 files changed, 31942 insertions(+), 31941 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..176a458f94 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/Common++/header/IpUtils.h b/Common++/header/IpUtils.h index 57088b2b6b..2f109d7f2a 100644 --- a/Common++/header/IpUtils.h +++ b/Common++/header/IpUtils.h @@ -1,91 +1,91 @@ -#ifndef PCAPPP_IP_UTILS -#define PCAPPP_IP_UTILS - -#include -#ifdef __linux__ -#include -#include -#endif -#if defined(__APPLE__) -#include -#include -#endif -#if defined(_WIN32) -#include -#endif -#if defined(__FreeBSD__) -#include -#include -#include -#endif - -/// @file - -// Both Visual C++ Compiler and MinGW-w64 define inet_ntop() and inet_pton() -// Add compatibility functions for old MinGW (aka MinGW32) -// We use "__MINGW64_VERSION_MAJOR" and not __MINGW64__ to detect MinGW-w64 compiler -// because the second one is not defined for MinGW-w64 in 32bits mode -#if defined(_WIN32) && !defined(_MSC_VER) && (!defined(__MINGW64_VERSION_MAJOR) || (__MINGW64_VERSION_MAJOR < 8)) -/** - * Convert a network format address to presentation format. - * @param[in] af Address family, can be either AF_INET (IPv4) or AF_INET6 (IPv6) - * @param[in] src Network address structure, can be either in_addr (IPv4) or in6_addr (IPv6) - * @param[out] dst Network address string representation - * @param[in] size 'dst' Maximum size - * @return pointer to presentation format address ('dst'), or NULL (see errno). - */ -const char* inet_ntop(int af, const void* src, char* dst, size_t size); - -/** - * Convert from presentation format (which usually means ASCII printable) - * to network format (which is usually some kind of binary format). - * @param[in] af Address family, can be either AF_INET (IPv4) or AF_INET6 (IPv6) - * @param[in] src Network address string representation - * @param[out] dst Network address structure result, can be either in_addr (IPv4) or in6_addr (IPv6) - * @return - * 1 if the address was valid for the specified address family; - * 0 if the address wasn't valid ('dst' is untouched in this case); - * -1 if some other error occurred ('dst' is untouched in this case, too) - */ -int inet_pton(int af, const char* src, void* dst); -#endif - - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - namespace internal - { - /** - * Extract IPv4 address from sockaddr - * @param[in] sa - input sockaddr - * @return Address in in_addr format - */ - in_addr* sockaddr2in_addr(struct sockaddr *sa); - - /** - * Extract IPv6 address from sockaddr - * @param[in] sa - input sockaddr - * @return Address in in6_addr format - */ - in6_addr* sockaddr2in6_addr(struct sockaddr *sa); - - /** - * Converts a sockaddr format address to its string representation - * @param[in] sa Address in sockaddr format - * @param[out] resultString String representation of the address - */ - void sockaddr2string(struct sockaddr *sa, char* resultString); - - /** - * Convert a in_addr format address to 32bit representation - * @param[in] inAddr Address in in_addr format - * @return Address in 32bit format - */ - uint32_t in_addr2int(in_addr inAddr); - } // namespace internal -} // namespace pcpp -#endif +#ifndef PCAPPP_IP_UTILS +#define PCAPPP_IP_UTILS + +#include +#ifdef __linux__ +#include +#include +#endif +#if defined(__APPLE__) +#include +#include +#endif +#if defined(_WIN32) +#include +#endif +#if defined(__FreeBSD__) +#include +#include +#include +#endif + +/// @file + +// Both Visual C++ Compiler and MinGW-w64 define inet_ntop() and inet_pton() +// Add compatibility functions for old MinGW (aka MinGW32) +// We use "__MINGW64_VERSION_MAJOR" and not __MINGW64__ to detect MinGW-w64 compiler +// because the second one is not defined for MinGW-w64 in 32bits mode +#if defined(_WIN32) && !defined(_MSC_VER) && (!defined(__MINGW64_VERSION_MAJOR) || (__MINGW64_VERSION_MAJOR < 8)) +/** + * Convert a network format address to presentation format. + * @param[in] af Address family, can be either AF_INET (IPv4) or AF_INET6 (IPv6) + * @param[in] src Network address structure, can be either in_addr (IPv4) or in6_addr (IPv6) + * @param[out] dst Network address string representation + * @param[in] size 'dst' Maximum size + * @return pointer to presentation format address ('dst'), or NULL (see errno). + */ +const char* inet_ntop(int af, const void* src, char* dst, size_t size); + +/** + * Convert from presentation format (which usually means ASCII printable) + * to network format (which is usually some kind of binary format). + * @param[in] af Address family, can be either AF_INET (IPv4) or AF_INET6 (IPv6) + * @param[in] src Network address string representation + * @param[out] dst Network address structure result, can be either in_addr (IPv4) or in6_addr (IPv6) + * @return + * 1 if the address was valid for the specified address family; + * 0 if the address wasn't valid ('dst' is untouched in this case); + * -1 if some other error occurred ('dst' is untouched in this case, too) + */ +int inet_pton(int af, const char* src, void* dst); +#endif + + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + namespace internal + { + /** + * Extract IPv4 address from sockaddr + * @param[in] sa - input sockaddr + * @return Address in in_addr format + */ + in_addr* sockaddr2in_addr(struct sockaddr *sa); + + /** + * Extract IPv6 address from sockaddr + * @param[in] sa - input sockaddr + * @return Address in in6_addr format + */ + in6_addr* sockaddr2in6_addr(struct sockaddr *sa); + + /** + * Converts a sockaddr format address to its string representation + * @param[in] sa Address in sockaddr format + * @param[out] resultString String representation of the address + */ + void sockaddr2string(struct sockaddr *sa, char* resultString); + + /** + * Convert a in_addr format address to 32bit representation + * @param[in] inAddr Address in in_addr format + * @return Address in 32bit format + */ + uint32_t in_addr2int(in_addr inAddr); + } // namespace internal +} // namespace pcpp +#endif diff --git a/Common++/header/Logger.h b/Common++/header/Logger.h index debbcb9c76..5113e8056c 100644 --- a/Common++/header/Logger.h +++ b/Common++/header/Logger.h @@ -1,250 +1,250 @@ -#ifndef PCAPPP_LOGGER -#define PCAPPP_LOGGER - -#include -#include -#include -#include -#include - -#ifndef LOG_MODULE -#define LOG_MODULE UndefinedLogModule -#endif - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - - /** - * An enum representing all PcapPlusPlus modules - */ - enum LogModule - { - UndefinedLogModule, - CommonLogModuleIpUtils, ///< IP Utils module (Common++) - CommonLogModuleTablePrinter, ///< Table printer module (Common++) - CommonLogModuleGenericUtils, ///< Generic Utils (Common++) - PacketLogModuleRawPacket, ///< RawPacket module (Packet++) - PacketLogModulePacket, ///< Packet module (Packet++) - PacketLogModuleLayer, ///< Layer module (Packet++) - PacketLogModuleArpLayer, ///< ArpLayer module (Packet++) - PacketLogModuleEthLayer, ///< EthLayer module (Packet++) - PacketLogModuleIPv4Layer, ///< IPv4Layer module (Packet++) - PacketLogModuleIPv6Layer, ///< IPv6Layer module (Packet++) - PacketLogModulePayloadLayer, ///< PayloadLayer module (Packet++) - PacketLogModuleTcpLayer, ///< TcpLayer module (Packet++) - PacketLogModuleUdpLayer, ///< UdpLayer module (Packet++) - PacketLogModuleVlanLayer, ///< VlanLayer module (Packet++) - PacketLogModuleHttpLayer, ///< HttpLayer module (Packet++) - PacketLogModulePPPoELayer, ///< PPPoELayer module (Packet++) - PacketLogModuleDnsLayer, ///< DnsLayer module (Packet++) - PacketLogModuleMplsLayer, ///< MplsLayer module (Packet++) - PacketLogModuleIcmpLayer, ///< IcmpLayer module (Packet++) - PacketLogModuleIcmpV6Layer, ///< IcmpV6Layer module (Packet++) - PacketLogModuleGreLayer, ///< GreLayer module (Packet++) - PacketLogModuleSSLLayer, ///< SSLLayer module (Packet++) - PacketLogModuleSllLayer, ///< SllLayer module (Packet++) - PacketLogModuleDhcpLayer, ///< DhcpLayer module (Packet++) - PacketLogModuleDhcpV6Layer, ///< DhcpV6Layer module (Packet++) - PacketLogModuleIgmpLayer, ///< IgmpLayer module (Packet++) - PacketLogModuleSipLayer, ///< SipLayer module (Packet++) - PacketLogModuleSdpLayer, ///< SdpLayer module (Packet++) - PacketLogModuleRadiusLayer, ///< RadiusLayer module (Packet++) - PacketLogModuleGtpLayer, ///< GtpLayer module (Packet++) - PacketLogModuleBgpLayer, ///< GtpLayer module (Packet++) - PacketLogModuleSSHLayer, ///< SSHLayer module (Packet++) - PacketLogModuleTcpReassembly, ///< TcpReassembly module (Packet++) - PacketLogModuleIPReassembly, ///< IPReassembly module (Packet++) - PacketLogModuleIPSecLayer, ///< IPSecLayers module (Packet++) - PacketLogModuleNtpLayer, ///< NtpLayer module (Packet++) - PacketLogModuleTelnetLayer, ///< TelnetLayer module (Packet++) - PacketLogModuleStpLayer, ///< StpLayer module (Packet++) - PacketLogModuleLLCLayer, ///< LLCLayer module (Packet++) - PacketLogModuleSingleCommandTextProtocolLayer, ///< SingleCommandTextProtocol module (Packet++) - PacketLogModuleNdpLayer, ///< NdpLayer module (Packet++) - PacketLogModuleFtpLayer, ///< FtpLayer module (Packet++) - PacketLogModuleSomeIpLayer, ///< SomeIpLayer module (Packet++) - PacketLogModuleSomeIpSdLayer, ///< SomeIpSdLayer module (Packet++) - PacketLogModuleWakeOnLanLayer, ///< WakeOnLanLayer module (Packet++) - PcapLogModuleWinPcapLiveDevice, ///< WinPcapLiveDevice module (Pcap++) - PcapLogModuleRemoteDevice, ///< WinPcapRemoteDevice module (Pcap++) - PcapLogModuleLiveDevice, ///< PcapLiveDevice module (Pcap++) - PcapLogModuleFileDevice, ///< FileDevice module (Pcap++) - PcapLogModulePfRingDevice, ///< PfRingDevice module (Pcap++) - PcapLogModuleMBufRawPacket, ///< MBufRawPacket module (Pcap++) - PcapLogModuleDpdkDevice, ///< DpdkDevice module (Pcap++) - PcapLogModuleKniDevice, ///< KniDevice module (Pcap++) - NetworkUtils, ///< NetworkUtils module (Pcap++) - NumOfLogModules - }; - - /** - * @class Logger - * PcapPlusPlus logger manager. - * PcapPlusPlus uses this logger to output both error and debug logs. - * There are currently 3 log levels: Logger#Error, Logger#Info and Logger#Debug. - * - * PcapPlusPlus is divided into modules (described in #LogModule enum). The user can set the log level got each module or to all modules at once. - * The default is Logger#Info which outputs only error messages. Changing log level for modules can be done dynamically while the application is running. - * - * The logger also exposes a method to retrieve the last error log message. - * - * Logs are printed to console by default in a certain format. The user can set a different print function to change the format or to print to - * other media (such as files, etc.). - * - * PcapPlusPlus logger is a singleton which can be reached from anywhere in the code. - * - * Note: Logger#Info level logs are currently only used in DPDK devices to set DPDK log level to RTE_LOG_NOTICE. - */ - class Logger - { - public: - /** - * An enum representing the log level. Currently 3 log levels are supported: Error, Info and Debug. Info is the default log level - */ - enum LogLevel - { - Error, ///< Error log level - Info, ///< Info log level - Debug ///< Debug log level - }; - - /** - * @typedef LogPrinter - * Log printer callback. Used for printing the logs in a custom way. - * @param[in] logLevel The log level for this log message - * @param[in] logMessage The log message - * @param[in] file The source file in PcapPlusPlus code the log message is coming from - * @param[in] method The method in PcapPlusPlus code the log message is coming from - * @param[in] line The line in PcapPlusPlus code the log message is coming from - */ - typedef void (*LogPrinter)(LogLevel logLevel, const std::string& logMessage, const std::string& file, const std::string& method, const int line); - - /** - * A static method for converting the log level enum to a string. - * @param[in] logLevel A log level enum - * @return The log level as a string - */ - static std::string logLevelAsString(LogLevel logLevel); - - /** - * Get the log level for a certain module - * @param[in] module PcapPlusPlus module - * @return The log level set for this module - */ - LogLevel getLogLevel(LogModule module) { return m_LogModulesArray[module]; } - - /** - * Set the log level for a certain PcapPlusPlus module - * @param[in] module PcapPlusPlus module - * @param[in] level The log level to set the module to - */ - void setLogLevel(LogModule module, LogLevel level) { m_LogModulesArray[module] = level; } - - /** - * Check whether a certain module is set to debug log level - * @param[in] module PcapPlusPlus module - * @return True if this module log level is "debug". False otherwise - */ - bool isDebugEnabled(LogModule module) const { return m_LogModulesArray[module] == Debug; } - - /** - * Set all PcapPlusPlus modules to a certain log level - * @param[in] level The log level to set all modules to - */ - void setAllModulesToLogLevel(LogLevel level) { for (int i=1; i - Logger& operator<<(const T& msg) - { - (*m_LogStream) << msg; - return *this; - } - - std::ostringstream * internalCreateLogStream(); - - /** - * An internal method to print log messages. Shouldn't be used externally. - */ - void internalPrintLogMessage(std::ostringstream* logStream, Logger::LogLevel logLevel, const char* file, const char* method, int line); - - /** - * Get access to Logger singleton - * @todo: make this singleton thread-safe/ - * @return a pointer to the Logger singleton - **/ - static Logger& getInstance() - { - static Logger instance; - return instance; - } - private: - bool m_LogsEnabled; - Logger::LogLevel m_LogModulesArray[NumOfLogModules]; - LogPrinter m_LogPrinter; - std::string m_LastError; - std::ostringstream* m_LogStream; - - // private c'tor - this class is a singleton - Logger(); - - static void defaultLogPrinter(LogLevel logLevel, const std::string& logMessage, const std::string& file, const std::string& method, const int line); - }; - -#define PCPP_LOG_DEBUG(message) do \ - { \ - if (pcpp::Logger::getInstance().logsEnabled() && pcpp::Logger::getInstance().isDebugEnabled(LOG_MODULE)) \ - { \ - std::ostringstream* sstream = pcpp::Logger::getInstance().internalCreateLogStream(); \ - (*sstream) << message; \ - pcpp::Logger::getInstance().internalPrintLogMessage(sstream, pcpp::Logger::Debug, __FILE__, __FUNCTION__, __LINE__); \ - } \ - } while(0) - -#define PCPP_LOG_ERROR(message) do \ - { \ - std::ostringstream* sstream = pcpp::Logger::getInstance().internalCreateLogStream(); \ - (*sstream) << message; \ - pcpp::Logger::getInstance().internalPrintLogMessage(sstream, pcpp::Logger::Error, __FILE__, __FUNCTION__, __LINE__); \ - } while (0) - -} // namespace pcpp - -#endif /* PCAPPP_LOGGER */ +#ifndef PCAPPP_LOGGER +#define PCAPPP_LOGGER + +#include +#include +#include +#include +#include + +#ifndef LOG_MODULE +#define LOG_MODULE UndefinedLogModule +#endif + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + + /** + * An enum representing all PcapPlusPlus modules + */ + enum LogModule + { + UndefinedLogModule, + CommonLogModuleIpUtils, ///< IP Utils module (Common++) + CommonLogModuleTablePrinter, ///< Table printer module (Common++) + CommonLogModuleGenericUtils, ///< Generic Utils (Common++) + PacketLogModuleRawPacket, ///< RawPacket module (Packet++) + PacketLogModulePacket, ///< Packet module (Packet++) + PacketLogModuleLayer, ///< Layer module (Packet++) + PacketLogModuleArpLayer, ///< ArpLayer module (Packet++) + PacketLogModuleEthLayer, ///< EthLayer module (Packet++) + PacketLogModuleIPv4Layer, ///< IPv4Layer module (Packet++) + PacketLogModuleIPv6Layer, ///< IPv6Layer module (Packet++) + PacketLogModulePayloadLayer, ///< PayloadLayer module (Packet++) + PacketLogModuleTcpLayer, ///< TcpLayer module (Packet++) + PacketLogModuleUdpLayer, ///< UdpLayer module (Packet++) + PacketLogModuleVlanLayer, ///< VlanLayer module (Packet++) + PacketLogModuleHttpLayer, ///< HttpLayer module (Packet++) + PacketLogModulePPPoELayer, ///< PPPoELayer module (Packet++) + PacketLogModuleDnsLayer, ///< DnsLayer module (Packet++) + PacketLogModuleMplsLayer, ///< MplsLayer module (Packet++) + PacketLogModuleIcmpLayer, ///< IcmpLayer module (Packet++) + PacketLogModuleIcmpV6Layer, ///< IcmpV6Layer module (Packet++) + PacketLogModuleGreLayer, ///< GreLayer module (Packet++) + PacketLogModuleSSLLayer, ///< SSLLayer module (Packet++) + PacketLogModuleSllLayer, ///< SllLayer module (Packet++) + PacketLogModuleDhcpLayer, ///< DhcpLayer module (Packet++) + PacketLogModuleDhcpV6Layer, ///< DhcpV6Layer module (Packet++) + PacketLogModuleIgmpLayer, ///< IgmpLayer module (Packet++) + PacketLogModuleSipLayer, ///< SipLayer module (Packet++) + PacketLogModuleSdpLayer, ///< SdpLayer module (Packet++) + PacketLogModuleRadiusLayer, ///< RadiusLayer module (Packet++) + PacketLogModuleGtpLayer, ///< GtpLayer module (Packet++) + PacketLogModuleBgpLayer, ///< GtpLayer module (Packet++) + PacketLogModuleSSHLayer, ///< SSHLayer module (Packet++) + PacketLogModuleTcpReassembly, ///< TcpReassembly module (Packet++) + PacketLogModuleIPReassembly, ///< IPReassembly module (Packet++) + PacketLogModuleIPSecLayer, ///< IPSecLayers module (Packet++) + PacketLogModuleNtpLayer, ///< NtpLayer module (Packet++) + PacketLogModuleTelnetLayer, ///< TelnetLayer module (Packet++) + PacketLogModuleStpLayer, ///< StpLayer module (Packet++) + PacketLogModuleLLCLayer, ///< LLCLayer module (Packet++) + PacketLogModuleSingleCommandTextProtocolLayer, ///< SingleCommandTextProtocol module (Packet++) + PacketLogModuleNdpLayer, ///< NdpLayer module (Packet++) + PacketLogModuleFtpLayer, ///< FtpLayer module (Packet++) + PacketLogModuleSomeIpLayer, ///< SomeIpLayer module (Packet++) + PacketLogModuleSomeIpSdLayer, ///< SomeIpSdLayer module (Packet++) + PacketLogModuleWakeOnLanLayer, ///< WakeOnLanLayer module (Packet++) + PcapLogModuleWinPcapLiveDevice, ///< WinPcapLiveDevice module (Pcap++) + PcapLogModuleRemoteDevice, ///< WinPcapRemoteDevice module (Pcap++) + PcapLogModuleLiveDevice, ///< PcapLiveDevice module (Pcap++) + PcapLogModuleFileDevice, ///< FileDevice module (Pcap++) + PcapLogModulePfRingDevice, ///< PfRingDevice module (Pcap++) + PcapLogModuleMBufRawPacket, ///< MBufRawPacket module (Pcap++) + PcapLogModuleDpdkDevice, ///< DpdkDevice module (Pcap++) + PcapLogModuleKniDevice, ///< KniDevice module (Pcap++) + NetworkUtils, ///< NetworkUtils module (Pcap++) + NumOfLogModules + }; + + /** + * @class Logger + * PcapPlusPlus logger manager. + * PcapPlusPlus uses this logger to output both error and debug logs. + * There are currently 3 log levels: Logger#Error, Logger#Info and Logger#Debug. + * + * PcapPlusPlus is divided into modules (described in #LogModule enum). The user can set the log level got each module or to all modules at once. + * The default is Logger#Info which outputs only error messages. Changing log level for modules can be done dynamically while the application is running. + * + * The logger also exposes a method to retrieve the last error log message. + * + * Logs are printed to console by default in a certain format. The user can set a different print function to change the format or to print to + * other media (such as files, etc.). + * + * PcapPlusPlus logger is a singleton which can be reached from anywhere in the code. + * + * Note: Logger#Info level logs are currently only used in DPDK devices to set DPDK log level to RTE_LOG_NOTICE. + */ + class Logger + { + public: + /** + * An enum representing the log level. Currently 3 log levels are supported: Error, Info and Debug. Info is the default log level + */ + enum LogLevel + { + Error, ///< Error log level + Info, ///< Info log level + Debug ///< Debug log level + }; + + /** + * @typedef LogPrinter + * Log printer callback. Used for printing the logs in a custom way. + * @param[in] logLevel The log level for this log message + * @param[in] logMessage The log message + * @param[in] file The source file in PcapPlusPlus code the log message is coming from + * @param[in] method The method in PcapPlusPlus code the log message is coming from + * @param[in] line The line in PcapPlusPlus code the log message is coming from + */ + typedef void (*LogPrinter)(LogLevel logLevel, const std::string& logMessage, const std::string& file, const std::string& method, const int line); + + /** + * A static method for converting the log level enum to a string. + * @param[in] logLevel A log level enum + * @return The log level as a string + */ + static std::string logLevelAsString(LogLevel logLevel); + + /** + * Get the log level for a certain module + * @param[in] module PcapPlusPlus module + * @return The log level set for this module + */ + LogLevel getLogLevel(LogModule module) { return m_LogModulesArray[module]; } + + /** + * Set the log level for a certain PcapPlusPlus module + * @param[in] module PcapPlusPlus module + * @param[in] level The log level to set the module to + */ + void setLogLevel(LogModule module, LogLevel level) { m_LogModulesArray[module] = level; } + + /** + * Check whether a certain module is set to debug log level + * @param[in] module PcapPlusPlus module + * @return True if this module log level is "debug". False otherwise + */ + bool isDebugEnabled(LogModule module) const { return m_LogModulesArray[module] == Debug; } + + /** + * Set all PcapPlusPlus modules to a certain log level + * @param[in] level The log level to set all modules to + */ + void setAllModulesToLogLevel(LogLevel level) { for (int i=1; i + Logger& operator<<(const T& msg) + { + (*m_LogStream) << msg; + return *this; + } + + std::ostringstream * internalCreateLogStream(); + + /** + * An internal method to print log messages. Shouldn't be used externally. + */ + void internalPrintLogMessage(std::ostringstream* logStream, Logger::LogLevel logLevel, const char* file, const char* method, int line); + + /** + * Get access to Logger singleton + * @todo: make this singleton thread-safe/ + * @return a pointer to the Logger singleton + **/ + static Logger& getInstance() + { + static Logger instance; + return instance; + } + private: + bool m_LogsEnabled; + Logger::LogLevel m_LogModulesArray[NumOfLogModules]; + LogPrinter m_LogPrinter; + std::string m_LastError; + std::ostringstream* m_LogStream; + + // private c'tor - this class is a singleton + Logger(); + + static void defaultLogPrinter(LogLevel logLevel, const std::string& logMessage, const std::string& file, const std::string& method, const int line); + }; + +#define PCPP_LOG_DEBUG(message) do \ + { \ + if (pcpp::Logger::getInstance().logsEnabled() && pcpp::Logger::getInstance().isDebugEnabled(LOG_MODULE)) \ + { \ + std::ostringstream* sstream = pcpp::Logger::getInstance().internalCreateLogStream(); \ + (*sstream) << message; \ + pcpp::Logger::getInstance().internalPrintLogMessage(sstream, pcpp::Logger::Debug, __FILE__, __FUNCTION__, __LINE__); \ + } \ + } while(0) + +#define PCPP_LOG_ERROR(message) do \ + { \ + std::ostringstream* sstream = pcpp::Logger::getInstance().internalCreateLogStream(); \ + (*sstream) << message; \ + pcpp::Logger::getInstance().internalPrintLogMessage(sstream, pcpp::Logger::Error, __FILE__, __FUNCTION__, __LINE__); \ + } while (0) + +} // namespace pcpp + +#endif /* PCAPPP_LOGGER */ diff --git a/Common++/header/PcapPlusPlusVersion.h b/Common++/header/PcapPlusPlusVersion.h index 8241840613..3d94dc0d2b 100644 --- a/Common++/header/PcapPlusPlusVersion.h +++ b/Common++/header/PcapPlusPlusVersion.h @@ -1,54 +1,54 @@ -#ifndef PCAPPP_VERSION_H -#define PCAPPP_VERSION_H - -#include - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - #define PCAPPLUSPLUS_VERSION "22.11+" - #define PCAPPLUSPLUS_VERSION_OFFICIAL "non-official release" - - #define PCAPPLUSPLUS_VERSION_FULL "v" PCAPPLUSPLUS_VERSION " (" PCAPPLUSPLUS_VERSION_OFFICIAL ")" - - /** - * @return PcapPlusPlus current version, e.g: 22.11. Notice that for non-official releases (which were pulled from GitHub) the version will end with a '+'. - * For example: '22.11+' means non-official release but '22.11' means official release - */ - inline std::string getPcapPlusPlusVersion() { return PCAPPLUSPLUS_VERSION; } - - /** - * @return PcapPlusPlus long version string which includes the version and info whether it's an official or non-official release. For example: "v22.11+ (non-official release)" - * or "v22.11 (official release)" - */ - inline std::string getPcapPlusPlusVersionFull() { return PCAPPLUSPLUS_VERSION_FULL; } - - /** - * @return The build date and time in a format of "Mmm dd yyyy hh:mm:ss" - */ - inline std::string getBuildDateTime() { return std::string(__DATE__) + " " + std::string(__TIME__); } - - /** - * @return The Git commit (revision) the binaries are built from - */ - std::string getGitCommit(); - - /** - * @return The Git branch the binaries are built from - */ - std::string getGitBranch(); - - /** - * @return Git branch and commit the binaries are built from. - * Aggregates data from getGitCommit() and getGitBranch() - */ - std::string getGitInfo(); - -} - -#endif /* PCAPPP_VERSION_H */ +#ifndef PCAPPP_VERSION_H +#define PCAPPP_VERSION_H + +#include + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + #define PCAPPLUSPLUS_VERSION "22.11+" + #define PCAPPLUSPLUS_VERSION_OFFICIAL "non-official release" + + #define PCAPPLUSPLUS_VERSION_FULL "v" PCAPPLUSPLUS_VERSION " (" PCAPPLUSPLUS_VERSION_OFFICIAL ")" + + /** + * @return PcapPlusPlus current version, e.g: 22.11. Notice that for non-official releases (which were pulled from GitHub) the version will end with a '+'. + * For example: '22.11+' means non-official release but '22.11' means official release + */ + inline std::string getPcapPlusPlusVersion() { return PCAPPLUSPLUS_VERSION; } + + /** + * @return PcapPlusPlus long version string which includes the version and info whether it's an official or non-official release. For example: "v22.11+ (non-official release)" + * or "v22.11 (official release)" + */ + inline std::string getPcapPlusPlusVersionFull() { return PCAPPLUSPLUS_VERSION_FULL; } + + /** + * @return The build date and time in a format of "Mmm dd yyyy hh:mm:ss" + */ + inline std::string getBuildDateTime() { return std::string(__DATE__) + " " + std::string(__TIME__); } + + /** + * @return The Git commit (revision) the binaries are built from + */ + std::string getGitCommit(); + + /** + * @return The Git branch the binaries are built from + */ + std::string getGitBranch(); + + /** + * @return Git branch and commit the binaries are built from. + * Aggregates data from getGitCommit() and getGitBranch() + */ + std::string getGitInfo(); + +} + +#endif /* PCAPPP_VERSION_H */ diff --git a/Common++/header/SystemUtils.h b/Common++/header/SystemUtils.h index df8f2b669d..a138fe8743 100644 --- a/Common++/header/SystemUtils.h +++ b/Common++/header/SystemUtils.h @@ -1,396 +1,396 @@ -#ifndef PCAPPP_SYSTEM_UTILS -#define PCAPPP_SYSTEM_UTILS - -#include -#include -#include - -/// @file - -#define MAX_NUM_OF_CORES 32 - -#ifdef _MSC_VER -int gettimeofday(struct timeval * tp, struct timezone * tzp); -#endif - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - - /** - * @struct SystemCore - * Represents data of 1 CPU core. Current implementation supports up to 32 cores - */ - struct SystemCore - { - /** - * Core position in a 32-bit mask. For each core this attribute holds a 4B integer where only 1 bit is set, according to the core ID. - * For example: in core #0 the right-most bit will be set (meaning the number 0x01); - * in core #5 the 5th right-most bit will be set (meaning the number 0x20)... - */ - uint32_t Mask; - - /** - * Core ID - a value between 0 and 31 - */ - uint8_t Id; - - /** - * Overload of the comparison operator - * @return true if 2 addresses are equal. False otherwise - */ - bool operator==(const SystemCore& other) const { return Id == other.Id; } - }; - - /** - * @struct SystemCores - * Contains static representation to all 32 cores and a static array to map core ID (integer) to a SystemCore struct - */ - struct SystemCores - { - /** - * Static representation of core #0 - */ - static const SystemCore Core0; - /** - * Static representation of core #1 - */ - static const SystemCore Core1; - /** - * Static representation of core #2 - */ - static const SystemCore Core2; - /** - * Static representation of core #3 - */ - static const SystemCore Core3; - /** - * Static representation of core #4 - */ - static const SystemCore Core4; - /** - * Static representation of core #5 - */ - static const SystemCore Core5; - /** - * Static representation of core #6 - */ - static const SystemCore Core6; - /** - * Static representation of core #7 - */ - static const SystemCore Core7; - /** - * Static representation of core #8 - */ - static const SystemCore Core8; - /** - * Static representation of core #9 - */ - static const SystemCore Core9; - /** - * Static representation of core #10 - */ - static const SystemCore Core10; - /** - * Static representation of core #11 - */ - static const SystemCore Core11; - /** - * Static representation of core #12 - */ - static const SystemCore Core12; - /** - * Static representation of core #13 - */ - static const SystemCore Core13; - /** - * Static representation of core #14 - */ - static const SystemCore Core14; - /** - * Static representation of core #15 - */ - static const SystemCore Core15; - /** - * Static representation of core #16 - */ - static const SystemCore Core16; - /** - * Static representation of core #17 - */ - static const SystemCore Core17; - /** - * Static representation of core #18 - */ - static const SystemCore Core18; - /** - * Static representation of core #19 - */ - static const SystemCore Core19; - /** - * Static representation of core #20 - */ - static const SystemCore Core20; - /** - * Static representation of core #21 - */ - static const SystemCore Core21; - /** - * Static representation of core #22 - */ - static const SystemCore Core22; - /** - * Static representation of core #23 - */ - static const SystemCore Core23; - /** - * Static representation of core #24 - */ - static const SystemCore Core24; - /** - * Static representation of core #25 - */ - static const SystemCore Core25; - /** - * Static representation of core #26 - */ - static const SystemCore Core26; - /** - * Static representation of core #27 - */ - static const SystemCore Core27; - /** - * Static representation of core #28 - */ - static const SystemCore Core28; - /** - * Static representation of core #29 - */ - static const SystemCore Core29; - /** - * Static representation of core #30 - */ - static const SystemCore Core30; - /** - * Static representation of core #31 - */ - static const SystemCore Core31; - - /** - * A static array for mapping core ID (integer) to the corresponding static SystemCore representation - */ - static const SystemCore IdToSystemCore[MAX_NUM_OF_CORES]; - }; - - typedef uint32_t CoreMask; - - /** - * Get total number of cores on device - * @return Total number of CPU cores on device - */ - int getNumOfCores(); - - /** - * Create a core mask for all cores available on machine - * @return A core mask for all cores available on machine - */ - CoreMask getCoreMaskForAllMachineCores(); - - - /** - * Create a core mask from a vector of system cores - * @param[in] cores A vector of SystemCore instances - * @return A core mask representing these cores - */ - CoreMask createCoreMaskFromCoreVector(std::vector cores); - - - /** - * Create a core mask from a vector of core IDs - * @param[in] coreIds A vector of core IDs - * @return A core mask representing these cores - */ - CoreMask createCoreMaskFromCoreIds(std::vector coreIds); - - - /** - * Convert a core mask into a vector of its appropriate system cores - * @param[in] coreMask The input core mask - * @param[out] resultVec The vector that will contain the system cores - */ - void createCoreVectorFromCoreMask(CoreMask coreMask, std::vector& resultVec); - - /** - * Execute a shell command and return its output - * @param[in] command The command to run - * @return The output of the command (both stdout and stderr) - */ - std::string executeShellCommand(const std::string &command); - - /** - * Check if a directory exists - * @param[in] dirPath Full path of the directory to search - * @return True if directory exists, false otherwise - */ - bool directoryExists(const std::string &dirPath); - - /** - * Retrieve a system-wide real-time accurate clock. It's actually a multi-platform version of clock_gettime() which is - * fully supported only on Linux - * @param[out] sec The second portion of the time - * @param[out] nsec The nanosecond portion of the time - * @return 0 for success, or -1 for failure - */ - int clockGetTime(long& sec, long& nsec); - - /** - * A multi-platform version of the popular sleep method. This method simply runs the right sleep method, according to the platform - * it is running on. - * @param[in] seconds Number of seconds to sleep - */ - void multiPlatformSleep(uint32_t seconds); - - /** - * A multi-platform version of sleep in milliseconds resolution. This method simply runs the right sleep method, according to the platform - * it is running on. - * @param[in] milliseconds Number of milliseconds to sleep - */ - void multiPlatformMSleep(uint32_t milliseconds); - - /** - * A multi-platform version of `htons` which convert host to network byte order - * @param[in] host Value in host byte order - * @return Value in network byte order - */ - uint16_t hostToNet16(uint16_t host); - - /** - * A multi-platform version of `ntohs` which convert network to host byte order - * @param[in] net Value in network byte order - * @return Value in host byte order - */ - uint16_t netToHost16(uint16_t net); - - /** - * A multi-platform version of `htonl` which convert host to network byte order - * @param[in] host Value in host byte order - * @return Value in network byte order - */ - uint32_t hostToNet32(uint32_t host); - - /** - * A multi-platform version of `ntohl` which convert network to host byte order - * @param[in] net Value in network byte order - * @return Value in host byte order - */ - uint32_t netToHost32(uint32_t net); - - /** - * @class AppName - * This class extracts the application name from the current running executable and stores it for usage of the application throughout its runtime. - * This class should be initialized once in the beginning of the main() method using AppName#init() and from then on the app name could be retrieved using AppName#get() - */ - class AppName - { - private: - static std::string m_AppName; - - public: - /** - * Static init method which should be called once at the beginning of the main method. - * @param[in] argc The argc param from main() - * @param[in] argv The argv param from main() - * @return No return value - */ - // cppcheck-suppress constParameter - static void init(int argc, char* argv[]) - { - if (argc == 0) - { - m_AppName.clear(); - return; - } - - m_AppName = argv[0]; - - // remove Linux/Unix path - size_t lastPos = m_AppName.rfind('/'); - if (lastPos != std::string::npos) - { - m_AppName = m_AppName.substr(lastPos + 1); - } - - // remove Windows path - lastPos = m_AppName.rfind('\\'); - if (lastPos != std::string::npos) - { - m_AppName = m_AppName.substr(lastPos + 1); - } - - // remove file extension - lastPos = m_AppName.rfind('.'); - if (lastPos != std::string::npos) { - m_AppName.resize(lastPos); - } - } - - /** - * @return The app name as extracted from the current running executable - */ - static const std::string& get() { return m_AppName; } - }; - - /** - * @class ApplicationEventHandler - * A singleton class that provides callbacks for events that occur during application life-cycle such as ctrl+c pressed, - * application closed, killed, etc. - */ - class ApplicationEventHandler - { - public: - /** - * @typedef EventHandlerCallback - * The callback to be invoked when the event occurs - * @param[in] cookie A pointer the the cookie provided by the user in ApplicationEventHandler c'tor - */ - typedef void (*EventHandlerCallback)(void* cookie); - - /** - * As ApplicationEventHandler is a singleton, this is the static getter to retrieve its instance - * @return The singleton instance of ApplicationEventHandler - */ - static ApplicationEventHandler& getInstance() - { - static ApplicationEventHandler instance; - return instance; - } - - /** - * Register for an application-interrupted event, meaning ctrl+c was pressed - * @param[in] handler The callback to be activated when the event occurs - * @param[in] cookie A pointer to a user provided object. This object will be transferred to the EventHandlerCallback callback. - * This cookie is very useful for transferring objects that give context to the event callback - */ - void onApplicationInterrupted(EventHandlerCallback handler, void* cookie); - - private: - EventHandlerCallback m_ApplicationInterruptedHandler; - void* m_ApplicationInterruptedCookie; - - // private c'tor - ApplicationEventHandler(); - -#if defined(_WIN32) - static int handlerRoutine(unsigned long fdwCtrlType); -#else - static void handlerRoutine(int signum); -#endif - }; - -} // namespace pcpp - -#endif /* PCAPPP_SYSTEM_UTILS */ +#ifndef PCAPPP_SYSTEM_UTILS +#define PCAPPP_SYSTEM_UTILS + +#include +#include +#include + +/// @file + +#define MAX_NUM_OF_CORES 32 + +#ifdef _MSC_VER +int gettimeofday(struct timeval * tp, struct timezone * tzp); +#endif + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + + /** + * @struct SystemCore + * Represents data of 1 CPU core. Current implementation supports up to 32 cores + */ + struct SystemCore + { + /** + * Core position in a 32-bit mask. For each core this attribute holds a 4B integer where only 1 bit is set, according to the core ID. + * For example: in core #0 the right-most bit will be set (meaning the number 0x01); + * in core #5 the 5th right-most bit will be set (meaning the number 0x20)... + */ + uint32_t Mask; + + /** + * Core ID - a value between 0 and 31 + */ + uint8_t Id; + + /** + * Overload of the comparison operator + * @return true if 2 addresses are equal. False otherwise + */ + bool operator==(const SystemCore& other) const { return Id == other.Id; } + }; + + /** + * @struct SystemCores + * Contains static representation to all 32 cores and a static array to map core ID (integer) to a SystemCore struct + */ + struct SystemCores + { + /** + * Static representation of core #0 + */ + static const SystemCore Core0; + /** + * Static representation of core #1 + */ + static const SystemCore Core1; + /** + * Static representation of core #2 + */ + static const SystemCore Core2; + /** + * Static representation of core #3 + */ + static const SystemCore Core3; + /** + * Static representation of core #4 + */ + static const SystemCore Core4; + /** + * Static representation of core #5 + */ + static const SystemCore Core5; + /** + * Static representation of core #6 + */ + static const SystemCore Core6; + /** + * Static representation of core #7 + */ + static const SystemCore Core7; + /** + * Static representation of core #8 + */ + static const SystemCore Core8; + /** + * Static representation of core #9 + */ + static const SystemCore Core9; + /** + * Static representation of core #10 + */ + static const SystemCore Core10; + /** + * Static representation of core #11 + */ + static const SystemCore Core11; + /** + * Static representation of core #12 + */ + static const SystemCore Core12; + /** + * Static representation of core #13 + */ + static const SystemCore Core13; + /** + * Static representation of core #14 + */ + static const SystemCore Core14; + /** + * Static representation of core #15 + */ + static const SystemCore Core15; + /** + * Static representation of core #16 + */ + static const SystemCore Core16; + /** + * Static representation of core #17 + */ + static const SystemCore Core17; + /** + * Static representation of core #18 + */ + static const SystemCore Core18; + /** + * Static representation of core #19 + */ + static const SystemCore Core19; + /** + * Static representation of core #20 + */ + static const SystemCore Core20; + /** + * Static representation of core #21 + */ + static const SystemCore Core21; + /** + * Static representation of core #22 + */ + static const SystemCore Core22; + /** + * Static representation of core #23 + */ + static const SystemCore Core23; + /** + * Static representation of core #24 + */ + static const SystemCore Core24; + /** + * Static representation of core #25 + */ + static const SystemCore Core25; + /** + * Static representation of core #26 + */ + static const SystemCore Core26; + /** + * Static representation of core #27 + */ + static const SystemCore Core27; + /** + * Static representation of core #28 + */ + static const SystemCore Core28; + /** + * Static representation of core #29 + */ + static const SystemCore Core29; + /** + * Static representation of core #30 + */ + static const SystemCore Core30; + /** + * Static representation of core #31 + */ + static const SystemCore Core31; + + /** + * A static array for mapping core ID (integer) to the corresponding static SystemCore representation + */ + static const SystemCore IdToSystemCore[MAX_NUM_OF_CORES]; + }; + + typedef uint32_t CoreMask; + + /** + * Get total number of cores on device + * @return Total number of CPU cores on device + */ + int getNumOfCores(); + + /** + * Create a core mask for all cores available on machine + * @return A core mask for all cores available on machine + */ + CoreMask getCoreMaskForAllMachineCores(); + + + /** + * Create a core mask from a vector of system cores + * @param[in] cores A vector of SystemCore instances + * @return A core mask representing these cores + */ + CoreMask createCoreMaskFromCoreVector(std::vector cores); + + + /** + * Create a core mask from a vector of core IDs + * @param[in] coreIds A vector of core IDs + * @return A core mask representing these cores + */ + CoreMask createCoreMaskFromCoreIds(std::vector coreIds); + + + /** + * Convert a core mask into a vector of its appropriate system cores + * @param[in] coreMask The input core mask + * @param[out] resultVec The vector that will contain the system cores + */ + void createCoreVectorFromCoreMask(CoreMask coreMask, std::vector& resultVec); + + /** + * Execute a shell command and return its output + * @param[in] command The command to run + * @return The output of the command (both stdout and stderr) + */ + std::string executeShellCommand(const std::string &command); + + /** + * Check if a directory exists + * @param[in] dirPath Full path of the directory to search + * @return True if directory exists, false otherwise + */ + bool directoryExists(const std::string &dirPath); + + /** + * Retrieve a system-wide real-time accurate clock. It's actually a multi-platform version of clock_gettime() which is + * fully supported only on Linux + * @param[out] sec The second portion of the time + * @param[out] nsec The nanosecond portion of the time + * @return 0 for success, or -1 for failure + */ + int clockGetTime(long& sec, long& nsec); + + /** + * A multi-platform version of the popular sleep method. This method simply runs the right sleep method, according to the platform + * it is running on. + * @param[in] seconds Number of seconds to sleep + */ + void multiPlatformSleep(uint32_t seconds); + + /** + * A multi-platform version of sleep in milliseconds resolution. This method simply runs the right sleep method, according to the platform + * it is running on. + * @param[in] milliseconds Number of milliseconds to sleep + */ + void multiPlatformMSleep(uint32_t milliseconds); + + /** + * A multi-platform version of `htons` which convert host to network byte order + * @param[in] host Value in host byte order + * @return Value in network byte order + */ + uint16_t hostToNet16(uint16_t host); + + /** + * A multi-platform version of `ntohs` which convert network to host byte order + * @param[in] net Value in network byte order + * @return Value in host byte order + */ + uint16_t netToHost16(uint16_t net); + + /** + * A multi-platform version of `htonl` which convert host to network byte order + * @param[in] host Value in host byte order + * @return Value in network byte order + */ + uint32_t hostToNet32(uint32_t host); + + /** + * A multi-platform version of `ntohl` which convert network to host byte order + * @param[in] net Value in network byte order + * @return Value in host byte order + */ + uint32_t netToHost32(uint32_t net); + + /** + * @class AppName + * This class extracts the application name from the current running executable and stores it for usage of the application throughout its runtime. + * This class should be initialized once in the beginning of the main() method using AppName#init() and from then on the app name could be retrieved using AppName#get() + */ + class AppName + { + private: + static std::string m_AppName; + + public: + /** + * Static init method which should be called once at the beginning of the main method. + * @param[in] argc The argc param from main() + * @param[in] argv The argv param from main() + * @return No return value + */ + // cppcheck-suppress constParameter + static void init(int argc, char* argv[]) + { + if (argc == 0) + { + m_AppName.clear(); + return; + } + + m_AppName = argv[0]; + + // remove Linux/Unix path + size_t lastPos = m_AppName.rfind('/'); + if (lastPos != std::string::npos) + { + m_AppName = m_AppName.substr(lastPos + 1); + } + + // remove Windows path + lastPos = m_AppName.rfind('\\'); + if (lastPos != std::string::npos) + { + m_AppName = m_AppName.substr(lastPos + 1); + } + + // remove file extension + lastPos = m_AppName.rfind('.'); + if (lastPos != std::string::npos) { + m_AppName.resize(lastPos); + } + } + + /** + * @return The app name as extracted from the current running executable + */ + static const std::string& get() { return m_AppName; } + }; + + /** + * @class ApplicationEventHandler + * A singleton class that provides callbacks for events that occur during application life-cycle such as ctrl+c pressed, + * application closed, killed, etc. + */ + class ApplicationEventHandler + { + public: + /** + * @typedef EventHandlerCallback + * The callback to be invoked when the event occurs + * @param[in] cookie A pointer the the cookie provided by the user in ApplicationEventHandler c'tor + */ + typedef void (*EventHandlerCallback)(void* cookie); + + /** + * As ApplicationEventHandler is a singleton, this is the static getter to retrieve its instance + * @return The singleton instance of ApplicationEventHandler + */ + static ApplicationEventHandler& getInstance() + { + static ApplicationEventHandler instance; + return instance; + } + + /** + * Register for an application-interrupted event, meaning ctrl+c was pressed + * @param[in] handler The callback to be activated when the event occurs + * @param[in] cookie A pointer to a user provided object. This object will be transferred to the EventHandlerCallback callback. + * This cookie is very useful for transferring objects that give context to the event callback + */ + void onApplicationInterrupted(EventHandlerCallback handler, void* cookie); + + private: + EventHandlerCallback m_ApplicationInterruptedHandler; + void* m_ApplicationInterruptedCookie; + + // private c'tor + ApplicationEventHandler(); + +#if defined(_WIN32) + static int handlerRoutine(unsigned long fdwCtrlType); +#else + static void handlerRoutine(int signum); +#endif + }; + +} // namespace pcpp + +#endif /* PCAPPP_SYSTEM_UTILS */ diff --git a/Common++/src/IpUtils.cpp b/Common++/src/IpUtils.cpp index 1aaee5a97d..c78a1807f5 100644 --- a/Common++/src/IpUtils.cpp +++ b/Common++/src/IpUtils.cpp @@ -1,393 +1,393 @@ -#define LOG_MODULE CommonLogModuleIpUtils - -#include "IpUtils.h" -#include "Logger.h" -#include -#include -#ifndef NS_INADDRSZ -#define NS_INADDRSZ 4 -#endif -#ifndef NS_IN6ADDRSZ -#define NS_IN6ADDRSZ 16 -#endif -#ifndef NS_INT16SZ -#define NS_INT16SZ 2 -#endif - -namespace pcpp -{ - namespace internal - { - in_addr* sockaddr2in_addr(struct sockaddr* sa) - { - if (sa == nullptr) - return nullptr; - if (sa->sa_family == AF_INET) - return &(((struct sockaddr_in*)sa)->sin_addr); - PCPP_LOG_DEBUG("sockaddr family is not AF_INET. Returning NULL"); - return nullptr; - } - - in6_addr* sockaddr2in6_addr(struct sockaddr* sa) - { - if (sa->sa_family == AF_INET6) - return &(((struct sockaddr_in6*)sa)->sin6_addr); - PCPP_LOG_DEBUG("sockaddr family is not AF_INET6. Returning NULL"); - return nullptr; - } - - void sockaddr2string(struct sockaddr* sa, char* resultString) - { - in_addr* ipv4Addr = sockaddr2in_addr(sa); - if (ipv4Addr != nullptr) - { - PCPP_LOG_DEBUG("IPv4 packet address"); - inet_ntop(AF_INET, &(((sockaddr_in*)sa)->sin_addr), resultString, INET_ADDRSTRLEN); - } - else - { - PCPP_LOG_DEBUG("Not IPv4 packet address. Assuming IPv6 packet"); - inet_ntop(AF_INET6, &(((sockaddr_in6*)sa)->sin6_addr), resultString, INET6_ADDRSTRLEN); - } - } - - uint32_t in_addr2int(in_addr inAddr) - { - #ifdef _WIN32 - return inAddr.S_un.S_addr; - #else - return inAddr.s_addr; - #endif - } - } // namespace internal -} // namespace pcpp - -// Only MinGW32 doesn't have these functions (not MinGW-w64 nor Visual C++) -#if defined(_WIN32) && !defined(_MSC_VER) && (!defined(__MINGW64_VERSION_MAJOR) || (__MINGW64_VERSION_MAJOR < 8)) -/* const char * - * inet_ntop4(src, dst, size) - * format an IPv4 address - * return: - * `dst' (as a const) - * notes: - * (1) uses no statistics - * (2) takes a u_char* not an in_addr as input - * author: - * Paul Vixie, 1996. - */ -static const char * -inet_ntop4(const uint8_t* src, char* dst, size_t size) -{ - static const char fmt[] = "%u.%u.%u.%u"; - char tmp[sizeof "255.255.255.255"]; - int nprinted; - nprinted = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); - /* Note: nprinted *excludes* the trailing '\0' character */ - if ((size_t)nprinted >= size) - { - return (NULL); - } - strncpy(dst, tmp, size); - return (dst); -} - -/* const char * - * inet_ntop6(src, dst, size) - * convert IPv6 binary address into presentation (printable) format - * author: - * Paul Vixie, 1996. - */ -static const char * -inet_ntop6(const uint8_t* src, char* dst, size_t size) -{ - /* - * Note that int32_t and int16_t need only be "at least" large enough - * to contain a value of the specified size. On some systems, like - * Crays, there is no such thing as an integer variable with 16 bits. - * Keep this in mind if you think this function should have been coded - * to use pointer overlays. All the world's not a VAX. - */ - char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; - struct { int base, len; } best, cur; - u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; - int i; - - /* - * Preprocess: - * Copy the input (bytewise) array into a wordwise array. - * Find the longest run of 0x00's in src[] for :: shorthanding. - */ - memset(words, '\0', sizeof words); - for (i = 0; i < NS_IN6ADDRSZ; i++) - words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); - best.base = -1; - best.len = 0; - cur.base = -1; - cur.len = 0; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) - { - if (words[i] == 0) - { - if (cur.base == -1) - cur.base = i, cur.len = 1; - else - cur.len++; - } - else - { - if (cur.base != -1) - { - if (best.base == -1 || cur.len > best.len) - best = cur; - cur.base = -1; - } - } - } - if (cur.base != -1) - { - if (best.base == -1 || cur.len > best.len) - best = cur; - } - if (best.base != -1 && best.len < 2) - best.base = -1; - - /* - * Format the result. - */ - tp = tmp; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) - { - /* Are we inside the best run of 0x00's? */ - if (best.base != -1 && i >= best.base && i < (best.base + best.len)) - { - if (i == best.base) - *tp++ = ':'; - continue; - } - /* Are we following an initial run of 0x00s or any real hex? */ - if (i != 0) - *tp++ = ':'; - /* Is this address an encapsulated IPv4? */ - if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) - { - if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) - return (NULL); - tp += strlen(tp); - break; - } - tp += snprintf(tp, (unsigned long) (sizeof tmp - (tp - tmp)), "%x", words[i]); - } - /* Was it a trailing run of 0x00's? */ - if (best.base != -1 && (best.base + best.len) == - (NS_IN6ADDRSZ / NS_INT16SZ)) - *tp++ = ':'; - *tp++ = '\0'; - - /* - * Check for overflow, copy, and we're done. - */ - if ((size_t)(tp - tmp) > size) - { - return (NULL); - } - strncpy(dst, tmp, size); - return (dst); -} - - -/* int - * inet_pton4(src, dst) - * like inet_aton() but without all the hexadecimal and shorthand. - * return: - * 1 if `src' is a valid dotted quad, else 0. - * notice: - * does not touch `dst' unless it's returning 1. - * author: - * Paul Vixie, 1996. - */ -static int -inet_pton4(const char* src, uint8_t* dst) -{ - static const char digits[] = "0123456789"; - int saw_digit, octets, ch; - u_char tmp[NS_INADDRSZ], *tp; - - saw_digit = 0; - octets = 0; - *(tp = tmp) = 0; - while ((ch = *src++) != '\0') - { - const char *pch; - - if ((pch = strchr(digits, ch)) != NULL) - { - size_t newSize = *tp * 10 + (pch - digits); - - if (newSize > 255) - return (0); - *tp = (u_char) newSize; - if (! saw_digit) - { - if (++octets > 4) - return (0); - saw_digit = 1; - } - } - else if (ch == '.' && saw_digit) - { - if (octets == 4) - return (0); - *++tp = 0; - saw_digit = 0; - } else - return (0); - } - if (octets < 4) - return (0); - memcpy(dst, tmp, NS_INADDRSZ); - return (1); -} - -/* int - * inet_pton6(src, dst) - * convert presentation level address to network order binary form. - * return: - * 1 if `src' is a valid [RFC1884 2.2] address, else 0. - * notice: - * (1) does not touch `dst' unless it's returning 1. - * (2) :: in a full address is silently ignored. - * credit: - * inspired by Mark Andrews. - * author: - * Paul Vixie, 1996. - */ -static int -inet_pton6(const char* src, uint8_t* dst) -{ - static const char xdigits_l[] = "0123456789abcdef", - xdigits_u[] = "0123456789ABCDEF"; - u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; - const char *curtok; - int ch, saw_xdigit; - u_int val; - - memset((tp = tmp), '\0', NS_IN6ADDRSZ); - endp = tp + NS_IN6ADDRSZ; - colonp = NULL; - /* Leading :: requires some special handling. */ - if (*src == ':') - if (*++src != ':') - return (0); - curtok = src; - saw_xdigit = 0; - val = 0; - while ((ch = *src++) != '\0') - { - const char* pch, *xdigits; - - if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) - pch = strchr((xdigits = xdigits_u), ch); - if (pch != NULL) - { - val <<= 4; - val |= (pch - xdigits); - if (val > 0xffff) - return (0); - saw_xdigit = 1; - continue; - } - if (ch == ':') - { - curtok = src; - if (!saw_xdigit) - { - if (colonp) - return (0); - colonp = tp; - continue; - } - else if (*src == '\0') - { - return (0); - } - if (tp + NS_INT16SZ > endp) - return (0); - *tp++ = (u_char) (val >> 8) & 0xff; - *tp++ = (u_char) val & 0xff; - saw_xdigit = 0; - val = 0; - continue; - } - if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) - { - tp += NS_INADDRSZ; - saw_xdigit = 0; - break; /* '\0' was seen by inet_pton4(). */ - } - return (0); - } - if (saw_xdigit) - { - if (tp + NS_INT16SZ > endp) - return (0); - *tp++ = (u_char) (val >> 8) & 0xff; - *tp++ = (u_char) val & 0xff; - } - if (colonp != NULL) - { - /* - * Since some memmove()'s erroneously fail to handle - * overlapping regions, we'll do the shift by hand. - */ - const int n = (int) (tp - colonp); - int i; - - if (tp == endp) - return (0); - for (i = 1; i <= n; i++) - { - endp[- i] = colonp[n - i]; - colonp[n - i] = 0; - } - tp = endp; - } - if (tp != endp) - return (0); - memcpy(dst, tmp, NS_IN6ADDRSZ); - return (1); -} - - -const char* inet_ntop(int af, const void* src, char* dst, size_t size) -{ - switch (af) - { - case AF_INET: - return (inet_ntop4((const uint8_t*)src, dst, size)); - case AF_INET6: - return (inet_ntop6((const uint8_t*)src, dst, size)); - default: - return (NULL); - } - /* NOTREACHED */ -} - -int inet_pton(int af, const char* src, void* dst) -{ - switch (af) - { -#ifdef AF_INET - case AF_INET: - return (inet_pton4(src, (uint8_t*)dst)); -#endif -#ifdef AF_INET6 - case AF_INET6: - return (inet_pton6(src, (uint8_t*)dst)); -#endif - default: - return (-1); - } - /* NOTREACHED */ -} - -#endif +#define LOG_MODULE CommonLogModuleIpUtils + +#include "IpUtils.h" +#include "Logger.h" +#include +#include +#ifndef NS_INADDRSZ +#define NS_INADDRSZ 4 +#endif +#ifndef NS_IN6ADDRSZ +#define NS_IN6ADDRSZ 16 +#endif +#ifndef NS_INT16SZ +#define NS_INT16SZ 2 +#endif + +namespace pcpp +{ + namespace internal + { + in_addr* sockaddr2in_addr(struct sockaddr* sa) + { + if (sa == nullptr) + return nullptr; + if (sa->sa_family == AF_INET) + return &(((struct sockaddr_in*)sa)->sin_addr); + PCPP_LOG_DEBUG("sockaddr family is not AF_INET. Returning NULL"); + return nullptr; + } + + in6_addr* sockaddr2in6_addr(struct sockaddr* sa) + { + if (sa->sa_family == AF_INET6) + return &(((struct sockaddr_in6*)sa)->sin6_addr); + PCPP_LOG_DEBUG("sockaddr family is not AF_INET6. Returning NULL"); + return nullptr; + } + + void sockaddr2string(struct sockaddr* sa, char* resultString) + { + in_addr* ipv4Addr = sockaddr2in_addr(sa); + if (ipv4Addr != nullptr) + { + PCPP_LOG_DEBUG("IPv4 packet address"); + inet_ntop(AF_INET, &(((sockaddr_in*)sa)->sin_addr), resultString, INET_ADDRSTRLEN); + } + else + { + PCPP_LOG_DEBUG("Not IPv4 packet address. Assuming IPv6 packet"); + inet_ntop(AF_INET6, &(((sockaddr_in6*)sa)->sin6_addr), resultString, INET6_ADDRSTRLEN); + } + } + + uint32_t in_addr2int(in_addr inAddr) + { + #ifdef _WIN32 + return inAddr.S_un.S_addr; + #else + return inAddr.s_addr; + #endif + } + } // namespace internal +} // namespace pcpp + +// Only MinGW32 doesn't have these functions (not MinGW-w64 nor Visual C++) +#if defined(_WIN32) && !defined(_MSC_VER) && (!defined(__MINGW64_VERSION_MAJOR) || (__MINGW64_VERSION_MAJOR < 8)) +/* const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address + * return: + * `dst' (as a const) + * notes: + * (1) uses no statistics + * (2) takes a u_char* not an in_addr as input + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop4(const uint8_t* src, char* dst, size_t size) +{ + static const char fmt[] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + int nprinted; + nprinted = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); + /* Note: nprinted *excludes* the trailing '\0' character */ + if ((size_t)nprinted >= size) + { + return (NULL); + } + strncpy(dst, tmp, size); + return (dst); +} + +/* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop6(const uint8_t* src, char* dst, size_t size) +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; + struct { int base, len; } best, cur; + u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + best.len = 0; + cur.base = -1; + cur.len = 0; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) + { + if (words[i] == 0) + { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } + else + { + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) + { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && i < (best.base + best.len)) + { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) + { + if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; + } + tp += snprintf(tp, (unsigned long) (sizeof tmp - (tp - tmp)), "%x", words[i]); + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == + (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t)(tp - tmp) > size) + { + return (NULL); + } + strncpy(dst, tmp, size); + return (dst); +} + + +/* int + * inet_pton4(src, dst) + * like inet_aton() but without all the hexadecimal and shorthand. + * return: + * 1 if `src' is a valid dotted quad, else 0. + * notice: + * does not touch `dst' unless it's returning 1. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton4(const char* src, uint8_t* dst) +{ + static const char digits[] = "0123456789"; + int saw_digit, octets, ch; + u_char tmp[NS_INADDRSZ], *tp; + + saw_digit = 0; + octets = 0; + *(tp = tmp) = 0; + while ((ch = *src++) != '\0') + { + const char *pch; + + if ((pch = strchr(digits, ch)) != NULL) + { + size_t newSize = *tp * 10 + (pch - digits); + + if (newSize > 255) + return (0); + *tp = (u_char) newSize; + if (! saw_digit) + { + if (++octets > 4) + return (0); + saw_digit = 1; + } + } + else if (ch == '.' && saw_digit) + { + if (octets == 4) + return (0); + *++tp = 0; + saw_digit = 0; + } else + return (0); + } + if (octets < 4) + return (0); + memcpy(dst, tmp, NS_INADDRSZ); + return (1); +} + +/* int + * inet_pton6(src, dst) + * convert presentation level address to network order binary form. + * return: + * 1 if `src' is a valid [RFC1884 2.2] address, else 0. + * notice: + * (1) does not touch `dst' unless it's returning 1. + * (2) :: in a full address is silently ignored. + * credit: + * inspired by Mark Andrews. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton6(const char* src, uint8_t* dst) +{ + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; + const char *curtok; + int ch, saw_xdigit; + u_int val; + + memset((tp = tmp), '\0', NS_IN6ADDRSZ); + endp = tp + NS_IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + return (0); + curtok = src; + saw_xdigit = 0; + val = 0; + while ((ch = *src++) != '\0') + { + const char* pch, *xdigits; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) + { + val <<= 4; + val |= (pch - xdigits); + if (val > 0xffff) + return (0); + saw_xdigit = 1; + continue; + } + if (ch == ':') + { + curtok = src; + if (!saw_xdigit) + { + if (colonp) + return (0); + colonp = tp; + continue; + } + else if (*src == '\0') + { + return (0); + } + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + saw_xdigit = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) + { + tp += NS_INADDRSZ; + saw_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + return (0); + } + if (saw_xdigit) + { + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + } + if (colonp != NULL) + { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = (int) (tp - colonp); + int i; + + if (tp == endp) + return (0); + for (i = 1; i <= n; i++) + { + endp[- i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return (0); + memcpy(dst, tmp, NS_IN6ADDRSZ); + return (1); +} + + +const char* inet_ntop(int af, const void* src, char* dst, size_t size) +{ + switch (af) + { + case AF_INET: + return (inet_ntop4((const uint8_t*)src, dst, size)); + case AF_INET6: + return (inet_ntop6((const uint8_t*)src, dst, size)); + default: + return (NULL); + } + /* NOTREACHED */ +} + +int inet_pton(int af, const char* src, void* dst) +{ + switch (af) + { +#ifdef AF_INET + case AF_INET: + return (inet_pton4(src, (uint8_t*)dst)); +#endif +#ifdef AF_INET6 + case AF_INET6: + return (inet_pton6(src, (uint8_t*)dst)); +#endif + default: + return (-1); + } + /* NOTREACHED */ +} + +#endif diff --git a/Common++/src/SystemUtils.cpp b/Common++/src/SystemUtils.cpp index 3aa18bcd4e..664b15a7b7 100644 --- a/Common++/src/SystemUtils.cpp +++ b/Common++/src/SystemUtils.cpp @@ -1,386 +1,386 @@ -#include "SystemUtils.h" -#include "EndianPortable.h" - -#ifndef _MSC_VER -#include -#endif -#include -#include -#include -#include -#include -#include -#if defined(__APPLE__) -#include -#include -#endif - -#if defined(_WIN32) -#define POPEN _popen -#else -#define POPEN popen -#endif - -#if defined(_WIN32) -#define PCLOSE _pclose -#else -#define PCLOSE pclose -#endif - -#ifdef _MSC_VER -int gettimeofday(struct timeval * tp, struct timezone * tzp) -{ - // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's - static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); - - SYSTEMTIME system_time; - FILETIME file_time; - uint64_t time; - - GetSystemTime(&system_time); - SystemTimeToFileTime(&system_time, &file_time); - time = ((uint64_t)file_time.dwLowDateTime); - time += ((uint64_t)file_time.dwHighDateTime) << 32; - - tp->tv_sec = (long)((time - EPOCH) / 10000000L); - tp->tv_usec = (long)(system_time.wMilliseconds * 1000); - return 0; -} -#endif - -namespace pcpp -{ - -const SystemCore SystemCores::Core0 = { 0x01, 0 }; -const SystemCore SystemCores::Core1 = { 0x02, 1 }; -const SystemCore SystemCores::Core2 = { 0x04, 2 }; -const SystemCore SystemCores::Core3 = { 0x08, 3 }; -const SystemCore SystemCores::Core4 = { 0x10, 4 }; -const SystemCore SystemCores::Core5 = { 0x20, 5 }; -const SystemCore SystemCores::Core6 = { 0x40, 6 }; -const SystemCore SystemCores::Core7 = { 0x80, 7 }; -const SystemCore SystemCores::Core8 = { 0x100, 8 }; -const SystemCore SystemCores::Core9 = { 0x200, 9 }; -const SystemCore SystemCores::Core10 = { 0x400, 10 }; -const SystemCore SystemCores::Core11 = { 0x800, 11 }; -const SystemCore SystemCores::Core12 = { 0x1000, 12 }; -const SystemCore SystemCores::Core13 = { 0x2000, 13 }; -const SystemCore SystemCores::Core14 = { 0x4000, 14 }; -const SystemCore SystemCores::Core15 = { 0x8000, 15 }; -const SystemCore SystemCores::Core16 = { 0x10000, 16 }; -const SystemCore SystemCores::Core17 = { 0x20000, 17 }; -const SystemCore SystemCores::Core18 = { 0x40000, 18 }; -const SystemCore SystemCores::Core19 = { 0x80000, 19 }; -const SystemCore SystemCores::Core20 = { 0x100000, 20 }; -const SystemCore SystemCores::Core21 = { 0x200000, 21 }; -const SystemCore SystemCores::Core22 = { 0x400000, 22 }; -const SystemCore SystemCores::Core23 = { 0x800000, 23 }; -const SystemCore SystemCores::Core24 = { 0x1000000, 24 }; -const SystemCore SystemCores::Core25 = { 0x2000000, 25 }; -const SystemCore SystemCores::Core26 = { 0x4000000, 26 }; -const SystemCore SystemCores::Core27 = { 0x8000000, 27 }; -const SystemCore SystemCores::Core28 = { 0x10000000, 28 }; -const SystemCore SystemCores::Core29 = { 0x20000000, 29 }; -const SystemCore SystemCores::Core30 = { 0x40000000, 30 }; -const SystemCore SystemCores::Core31 = { 0x80000000, 31 }; - -const SystemCore SystemCores::IdToSystemCore[MAX_NUM_OF_CORES] = -{ - SystemCores::Core0, - SystemCores::Core1, - SystemCores::Core2, - SystemCores::Core3, - SystemCores::Core4, - SystemCores::Core5, - SystemCores::Core6, - SystemCores::Core7, - SystemCores::Core8, - SystemCores::Core9, - SystemCores::Core10, - SystemCores::Core11, - SystemCores::Core12, - SystemCores::Core13, - SystemCores::Core14, - SystemCores::Core15, - SystemCores::Core16, - SystemCores::Core17, - SystemCores::Core18, - SystemCores::Core19, - SystemCores::Core20, - SystemCores::Core21, - SystemCores::Core22, - SystemCores::Core23, - SystemCores::Core24, - SystemCores::Core25, - SystemCores::Core26, - SystemCores::Core27, - SystemCores::Core28, - SystemCores::Core29, - SystemCores::Core30, - SystemCores::Core31 -}; - - -int getNumOfCores() -{ -#if defined(_WIN32) - SYSTEM_INFO sysinfo; - GetSystemInfo( &sysinfo ); - return sysinfo.dwNumberOfProcessors; -#else - return sysconf(_SC_NPROCESSORS_ONLN); -#endif -} - -CoreMask getCoreMaskForAllMachineCores() -{ - int numOfCores = getNumOfCores() < 32 ? getNumOfCores() : 32; - CoreMask result = 0; - for (int i = 0; i < numOfCores; i++) - { - result = result | SystemCores::IdToSystemCore[i].Mask; - } - - return result; -} - -CoreMask createCoreMaskFromCoreVector(std::vector cores) -{ - CoreMask result = 0; - for (std::vector::iterator iter = cores.begin(); iter != cores.end(); iter++) - { - result |= iter->Mask; - } - - return result; -} - -CoreMask createCoreMaskFromCoreIds(std::vector coreIds) -{ - CoreMask result = 0; - for (std::vector::iterator iter = coreIds.begin(); iter != coreIds.end(); iter++) - { - result |= SystemCores::IdToSystemCore[*iter].Mask; - } - - return result; -} - -void createCoreVectorFromCoreMask(CoreMask coreMask, std::vector& resultVec) -{ - int i = 0; - while (coreMask != 0) - { - if (1 & coreMask) - { - resultVec.push_back(SystemCores::IdToSystemCore[i]); - } - - coreMask = coreMask >> 1; - i++; - } -} - -std::string executeShellCommand(const std::string &command) -{ - FILE* pipe = POPEN(command.c_str(), "r"); - if (!pipe) return "ERROR"; - char buffer[128]; - std::string result = ""; - while(!feof(pipe)) - { - if(fgets(buffer, 128, pipe) != nullptr) - result += buffer; - } - PCLOSE(pipe); - return result; -} - - -bool directoryExists(const std::string &dirPath) -{ - struct stat info; - - if (stat(dirPath.c_str(), &info) != 0) - return false; - else if(info.st_mode & S_IFDIR) - return true; - else - return false; -} - - -int clockGetTime(long& sec, long& nsec) -{ - sec = 0; - nsec = 0; - -#if defined(_WIN32) - -#define CLOCK_GETTIME_BILLION (1E9) - - static BOOL clock_gettime_first_time = 1; - static LARGE_INTEGER clock_gettime_counts_per_sec; - - LARGE_INTEGER count ; - - if (clock_gettime_first_time) - { - clock_gettime_first_time = 0; - - if (0 == QueryPerformanceFrequency(&clock_gettime_counts_per_sec)) - { - clock_gettime_counts_per_sec.QuadPart = 0; - } - } - - if ((clock_gettime_counts_per_sec.QuadPart <= 0) || (0 == QueryPerformanceCounter(&count))) - { - return -1; - } - - sec = count.QuadPart / clock_gettime_counts_per_sec.QuadPart; - nsec = ((count.QuadPart % clock_gettime_counts_per_sec.QuadPart) * CLOCK_GETTIME_BILLION) / clock_gettime_counts_per_sec.QuadPart; - - return 0; - -#elif defined(__APPLE__) - - clock_serv_t cclock; - mach_timespec_t mts; - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - sec = mts.tv_sec; - nsec = mts.tv_nsec; - - return 0; - -#else // Linux - - #include - - timespec ts; - int res = clock_gettime(CLOCK_REALTIME, &ts); - if (res == 0) - { - sec = ts.tv_sec; - nsec = ts.tv_nsec; - } - return res; - -#endif -} - -void multiPlatformSleep(uint32_t seconds) -{ -#if defined(_WIN32) - Sleep(seconds*1000); -#else - sleep(seconds); -#endif -} - -void multiPlatformMSleep(uint32_t milliseconds) -{ -#if defined(_WIN32) - Sleep(milliseconds); -#else - usleep(milliseconds*1000); -#endif -} - -uint16_t hostToNet16(uint16_t host) -{ - return htobe16(host); -} - -uint16_t netToHost16(uint16_t net) -{ - return be16toh(net); -} - -uint32_t hostToNet32(uint32_t host) -{ - return htobe32(host); -} - -uint32_t netToHost32(uint32_t net) -{ - return be32toh(net); -} - - -std::string AppName::m_AppName; - -#if defined(_WIN32) -int ApplicationEventHandler::handlerRoutine(unsigned long fdwCtrlType) -{ - switch (fdwCtrlType) - { - case CTRL_C_EVENT: - case CTRL_BREAK_EVENT: - { - if (ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler != NULL) - ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler(ApplicationEventHandler::getInstance().m_ApplicationInterruptedCookie); - return TRUE; - } - - default: - return FALSE; - } - -} -#else - -static std::mutex UnixLinuxHandlerRoutineMutex; - -void ApplicationEventHandler::handlerRoutine(int signum) -{ - switch (signum) - { - case SIGINT: - { - // Most calls are unsafe in a signal handler, and this includes printf(). In particular, - // if the signal is caught while inside printf() it may be called twice at the same time which might not be a good idea - // The way to make sure the signal is called only once is using this lock and putting NULL in m_ApplicationInterruptedHandler - const std::lock_guard lock(UnixLinuxHandlerRoutineMutex); - - if (ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler != nullptr) - ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler(ApplicationEventHandler::getInstance().m_ApplicationInterruptedCookie); - - ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler = nullptr; - - return; - } - default: - { - return; - } - } -} -#endif - - -ApplicationEventHandler::ApplicationEventHandler() : - m_ApplicationInterruptedHandler(nullptr), m_ApplicationInterruptedCookie(nullptr) -{ -} - -void ApplicationEventHandler::onApplicationInterrupted(EventHandlerCallback handler, void* cookie) -{ - m_ApplicationInterruptedHandler = handler; - m_ApplicationInterruptedCookie = cookie; - -#if defined(_WIN32) - SetConsoleCtrlHandler((PHANDLER_ROUTINE)handlerRoutine, TRUE); -#else - struct sigaction action; - memset(&action, 0, sizeof(struct sigaction)); - action.sa_handler = handlerRoutine; - sigemptyset(&action.sa_mask); - sigaction(SIGINT, &action, nullptr); -#endif -} - -} // namespace pcpp +#include "SystemUtils.h" +#include "EndianPortable.h" + +#ifndef _MSC_VER +#include +#endif +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) +#include +#include +#endif + +#if defined(_WIN32) +#define POPEN _popen +#else +#define POPEN popen +#endif + +#if defined(_WIN32) +#define PCLOSE _pclose +#else +#define PCLOSE pclose +#endif + +#ifdef _MSC_VER +int gettimeofday(struct timeval * tp, struct timezone * tzp) +{ + // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + return 0; +} +#endif + +namespace pcpp +{ + +const SystemCore SystemCores::Core0 = { 0x01, 0 }; +const SystemCore SystemCores::Core1 = { 0x02, 1 }; +const SystemCore SystemCores::Core2 = { 0x04, 2 }; +const SystemCore SystemCores::Core3 = { 0x08, 3 }; +const SystemCore SystemCores::Core4 = { 0x10, 4 }; +const SystemCore SystemCores::Core5 = { 0x20, 5 }; +const SystemCore SystemCores::Core6 = { 0x40, 6 }; +const SystemCore SystemCores::Core7 = { 0x80, 7 }; +const SystemCore SystemCores::Core8 = { 0x100, 8 }; +const SystemCore SystemCores::Core9 = { 0x200, 9 }; +const SystemCore SystemCores::Core10 = { 0x400, 10 }; +const SystemCore SystemCores::Core11 = { 0x800, 11 }; +const SystemCore SystemCores::Core12 = { 0x1000, 12 }; +const SystemCore SystemCores::Core13 = { 0x2000, 13 }; +const SystemCore SystemCores::Core14 = { 0x4000, 14 }; +const SystemCore SystemCores::Core15 = { 0x8000, 15 }; +const SystemCore SystemCores::Core16 = { 0x10000, 16 }; +const SystemCore SystemCores::Core17 = { 0x20000, 17 }; +const SystemCore SystemCores::Core18 = { 0x40000, 18 }; +const SystemCore SystemCores::Core19 = { 0x80000, 19 }; +const SystemCore SystemCores::Core20 = { 0x100000, 20 }; +const SystemCore SystemCores::Core21 = { 0x200000, 21 }; +const SystemCore SystemCores::Core22 = { 0x400000, 22 }; +const SystemCore SystemCores::Core23 = { 0x800000, 23 }; +const SystemCore SystemCores::Core24 = { 0x1000000, 24 }; +const SystemCore SystemCores::Core25 = { 0x2000000, 25 }; +const SystemCore SystemCores::Core26 = { 0x4000000, 26 }; +const SystemCore SystemCores::Core27 = { 0x8000000, 27 }; +const SystemCore SystemCores::Core28 = { 0x10000000, 28 }; +const SystemCore SystemCores::Core29 = { 0x20000000, 29 }; +const SystemCore SystemCores::Core30 = { 0x40000000, 30 }; +const SystemCore SystemCores::Core31 = { 0x80000000, 31 }; + +const SystemCore SystemCores::IdToSystemCore[MAX_NUM_OF_CORES] = +{ + SystemCores::Core0, + SystemCores::Core1, + SystemCores::Core2, + SystemCores::Core3, + SystemCores::Core4, + SystemCores::Core5, + SystemCores::Core6, + SystemCores::Core7, + SystemCores::Core8, + SystemCores::Core9, + SystemCores::Core10, + SystemCores::Core11, + SystemCores::Core12, + SystemCores::Core13, + SystemCores::Core14, + SystemCores::Core15, + SystemCores::Core16, + SystemCores::Core17, + SystemCores::Core18, + SystemCores::Core19, + SystemCores::Core20, + SystemCores::Core21, + SystemCores::Core22, + SystemCores::Core23, + SystemCores::Core24, + SystemCores::Core25, + SystemCores::Core26, + SystemCores::Core27, + SystemCores::Core28, + SystemCores::Core29, + SystemCores::Core30, + SystemCores::Core31 +}; + + +int getNumOfCores() +{ +#if defined(_WIN32) + SYSTEM_INFO sysinfo; + GetSystemInfo( &sysinfo ); + return sysinfo.dwNumberOfProcessors; +#else + return sysconf(_SC_NPROCESSORS_ONLN); +#endif +} + +CoreMask getCoreMaskForAllMachineCores() +{ + int numOfCores = getNumOfCores() < 32 ? getNumOfCores() : 32; + CoreMask result = 0; + for (int i = 0; i < numOfCores; i++) + { + result = result | SystemCores::IdToSystemCore[i].Mask; + } + + return result; +} + +CoreMask createCoreMaskFromCoreVector(std::vector cores) +{ + CoreMask result = 0; + for (std::vector::iterator iter = cores.begin(); iter != cores.end(); iter++) + { + result |= iter->Mask; + } + + return result; +} + +CoreMask createCoreMaskFromCoreIds(std::vector coreIds) +{ + CoreMask result = 0; + for (std::vector::iterator iter = coreIds.begin(); iter != coreIds.end(); iter++) + { + result |= SystemCores::IdToSystemCore[*iter].Mask; + } + + return result; +} + +void createCoreVectorFromCoreMask(CoreMask coreMask, std::vector& resultVec) +{ + int i = 0; + while (coreMask != 0) + { + if (1 & coreMask) + { + resultVec.push_back(SystemCores::IdToSystemCore[i]); + } + + coreMask = coreMask >> 1; + i++; + } +} + +std::string executeShellCommand(const std::string &command) +{ + FILE* pipe = POPEN(command.c_str(), "r"); + if (!pipe) return "ERROR"; + char buffer[128]; + std::string result = ""; + while(!feof(pipe)) + { + if(fgets(buffer, 128, pipe) != nullptr) + result += buffer; + } + PCLOSE(pipe); + return result; +} + + +bool directoryExists(const std::string &dirPath) +{ + struct stat info; + + if (stat(dirPath.c_str(), &info) != 0) + return false; + else if(info.st_mode & S_IFDIR) + return true; + else + return false; +} + + +int clockGetTime(long& sec, long& nsec) +{ + sec = 0; + nsec = 0; + +#if defined(_WIN32) + +#define CLOCK_GETTIME_BILLION (1E9) + + static BOOL clock_gettime_first_time = 1; + static LARGE_INTEGER clock_gettime_counts_per_sec; + + LARGE_INTEGER count ; + + if (clock_gettime_first_time) + { + clock_gettime_first_time = 0; + + if (0 == QueryPerformanceFrequency(&clock_gettime_counts_per_sec)) + { + clock_gettime_counts_per_sec.QuadPart = 0; + } + } + + if ((clock_gettime_counts_per_sec.QuadPart <= 0) || (0 == QueryPerformanceCounter(&count))) + { + return -1; + } + + sec = count.QuadPart / clock_gettime_counts_per_sec.QuadPart; + nsec = ((count.QuadPart % clock_gettime_counts_per_sec.QuadPart) * CLOCK_GETTIME_BILLION) / clock_gettime_counts_per_sec.QuadPart; + + return 0; + +#elif defined(__APPLE__) + + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + sec = mts.tv_sec; + nsec = mts.tv_nsec; + + return 0; + +#else // Linux + + #include + + timespec ts; + int res = clock_gettime(CLOCK_REALTIME, &ts); + if (res == 0) + { + sec = ts.tv_sec; + nsec = ts.tv_nsec; + } + return res; + +#endif +} + +void multiPlatformSleep(uint32_t seconds) +{ +#if defined(_WIN32) + Sleep(seconds*1000); +#else + sleep(seconds); +#endif +} + +void multiPlatformMSleep(uint32_t milliseconds) +{ +#if defined(_WIN32) + Sleep(milliseconds); +#else + usleep(milliseconds*1000); +#endif +} + +uint16_t hostToNet16(uint16_t host) +{ + return htobe16(host); +} + +uint16_t netToHost16(uint16_t net) +{ + return be16toh(net); +} + +uint32_t hostToNet32(uint32_t host) +{ + return htobe32(host); +} + +uint32_t netToHost32(uint32_t net) +{ + return be32toh(net); +} + + +std::string AppName::m_AppName; + +#if defined(_WIN32) +int ApplicationEventHandler::handlerRoutine(unsigned long fdwCtrlType) +{ + switch (fdwCtrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + { + if (ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler != NULL) + ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler(ApplicationEventHandler::getInstance().m_ApplicationInterruptedCookie); + return TRUE; + } + + default: + return FALSE; + } + +} +#else + +static std::mutex UnixLinuxHandlerRoutineMutex; + +void ApplicationEventHandler::handlerRoutine(int signum) +{ + switch (signum) + { + case SIGINT: + { + // Most calls are unsafe in a signal handler, and this includes printf(). In particular, + // if the signal is caught while inside printf() it may be called twice at the same time which might not be a good idea + // The way to make sure the signal is called only once is using this lock and putting NULL in m_ApplicationInterruptedHandler + const std::lock_guard lock(UnixLinuxHandlerRoutineMutex); + + if (ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler != nullptr) + ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler(ApplicationEventHandler::getInstance().m_ApplicationInterruptedCookie); + + ApplicationEventHandler::getInstance().m_ApplicationInterruptedHandler = nullptr; + + return; + } + default: + { + return; + } + } +} +#endif + + +ApplicationEventHandler::ApplicationEventHandler() : + m_ApplicationInterruptedHandler(nullptr), m_ApplicationInterruptedCookie(nullptr) +{ +} + +void ApplicationEventHandler::onApplicationInterrupted(EventHandlerCallback handler, void* cookie) +{ + m_ApplicationInterruptedHandler = handler; + m_ApplicationInterruptedCookie = cookie; + +#if defined(_WIN32) + SetConsoleCtrlHandler((PHANDLER_ROUTINE)handlerRoutine, TRUE); +#else + struct sigaction action; + memset(&action, 0, sizeof(struct sigaction)); + action.sa_handler = handlerRoutine; + sigemptyset(&action.sa_mask); + sigaction(SIGINT, &action, nullptr); +#endif +} + +} // namespace pcpp diff --git a/Packet++/header/DhcpLayer.h b/Packet++/header/DhcpLayer.h index cd193fe527..e2932723e9 100644 --- a/Packet++/header/DhcpLayer.h +++ b/Packet++/header/DhcpLayer.h @@ -1,821 +1,821 @@ -#ifndef PACKETPP_DHCP_LAYER -#define PACKETPP_DHCP_LAYER - -#include "Layer.h" -#include "TLVData.h" -#include "IpAddress.h" -#include "MacAddress.h" -#include - -#ifndef PCPP_DEPRECATED -#if defined(__GNUC__) || defined(__clang__) -#define PCPP_DEPRECATED __attribute__((deprecated)) -#elif defined(_MSC_VER) -#define PCPP_DEPRECATED __declspec(deprecated) -#else -#pragma message("WARNING: DEPRECATED feature is not implemented for this compiler") -#define PCPP_DEPRECATED -#endif -#endif - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - - /** - * @struct dhcp_header - * Represents a DHCP protocol header - */ - #pragma pack(push, 1) - struct dhcp_header - { - /** BootP opcode */ - uint8_t opCode; - /** Hardware type, set to 1 (Ethernet) by default */ - uint8_t hardwareType; - /** Hardware address length, set to 6 (MAC address length) by default */ - uint8_t hardwareAddressLength; - /** Hop count */ - uint8_t hops; - /** DHCP/BootP transaction ID */ - uint32_t transactionID; - /** The elapsed time, in seconds since the client sent its first BOOTREQUEST message */ - uint16_t secondsElapsed; - /** BootP flags */ - uint16_t flags; - /** Client IPv4 address */ - uint32_t clientIpAddress; - /** Your IPv4 address */ - uint32_t yourIpAddress; - /** Server IPv4 address */ - uint32_t serverIpAddress; - /** Gateway IPv4 address */ - uint32_t gatewayIpAddress; - /** Client hardware address, by default contains the MAC address (only 6 first bytes are used) */ - uint8_t clientHardwareAddress[16]; - /** BootP server name */ - uint8_t serverName[64]; - /** BootP boot file name */ - uint8_t bootFilename[128]; - /** DHCP magic number (set to the default value of 0x63538263) */ - uint32_t magicNumber; - }; - #pragma pack(pop) - - - /** - * BootP opcodes - */ - enum BootpOpCodes - { - /** BootP request */ - DHCP_BOOTREQUEST = 1, - /** BootP reply */ - DHCP_BOOTREPLY = 2 - }; - - /** - * DHCP message types - */ - enum DhcpMessageType - { - /** Unknown message type */ - DHCP_UNKNOWN_MSG_TYPE = 0, - /** Discover message type */ - DHCP_DISCOVER = 1, - /** Offer message type */ - DHCP_OFFER = 2, - /** Request message type */ - DHCP_REQUEST = 3, - /** Decline message type */ - DHCP_DECLINE = 4, - /** Acknowledge message type */ - DHCP_ACK = 5, - /** Non-acknowledge message type */ - DHCP_NAK = 6, - /** Release message type */ - DHCP_RELEASE = 7, - /** Inform message type */ - DHCP_INFORM = 8 - }; - - /** - * DHCP option types. - */ - enum DhcpOptionTypes - { - /** Unknown option type */ - DHCPOPT_UNKNOWN = -1, - /** Pad */ - DHCPOPT_PAD = 0, - /** Subnet Mask Value */ - DHCPOPT_SUBNET_MASK = 1, - /** Time Offset in Seconds from UTC */ - DHCPOPT_TIME_OFFSET = 2, - /** N/4 Router addresses */ - DHCPOPT_ROUTERS = 3, - /** N/4 Timeserver addresses */ - DHCPOPT_TIME_SERVERS = 4, - /** N/4 IEN-116 Server addresses */ - DHCPOPT_NAME_SERVERS = 5, - /** N/4 DNS Server addresses */ - DHCPOPT_DOMAIN_NAME_SERVERS = 6, - /** N/4 Logging Server addresses */ - DHCPOPT_LOG_SERVERS = 7, - /** N/4 Quotes Server addresses */ - DHCPOPT_QUOTES_SERVERS = 8, - /** N/4 Quotes Server addresses */ - DHCPOPT_LPR_SERVERS = 9, - /** N/4 Quotes Server addresses */ - DHCPOPT_IMPRESS_SERVERS = 10, - /** N/4 RLP Server addresses */ - DHCPOPT_RESOURCE_LOCATION_SERVERS = 11, - /** Hostname string */ - DHCPOPT_HOST_NAME = 12, - /** Size of boot file in 512 byte chunks */ - DHCPOPT_BOOT_SIZE = 13, - /** Client to dump and name the file to dump it to */ - DHCPOPT_MERIT_DUMP = 14, - /** The DNS domain name of the client */ - DHCPOPT_DOMAIN_NAME = 15, - /** Swap Server address */ - DHCPOPT_SWAP_SERVER = 16, - /** Path name for root disk */ - DHCPOPT_ROOT_PATH = 17, - /** Path name for more BOOTP info */ - DHCPOPT_EXTENSIONS_PATH = 18, - /** Enable/Disable IP Forwarding */ - DHCPOPT_IP_FORWARDING = 19, - /** Enable/Disable Source Routing */ - DHCPOPT_NON_LOCAL_SOURCE_ROUTING = 20, - /** Routing Policy Filters */ - DHCPOPT_POLICY_FILTER = 21, - /** Max Datagram Reassembly Size */ - DHCPOPT_MAX_DGRAM_REASSEMBLY = 22, - /** Default IP Time to Live */ - DEFAULT_IP_TTL = 23, - /** Path MTU Aging Timeout */ - DHCPOPT_PATH_MTU_AGING_TIMEOUT = 24, - /** Path MTU Plateau Table */ - PATH_MTU_PLATEAU_TABLE = 25, - /** Interface MTU Size */ - DHCPOPT_INTERFACE_MTU = 26, - /** All Subnets are Local */ - DHCPOPT_ALL_SUBNETS_LOCAL = 27, - /** Broadcast Address */ - DHCPOPT_BROADCAST_ADDRESS = 28, - /** Perform Mask Discovery */ - DHCPOPT_PERFORM_MASK_DISCOVERY = 29, - /** Provide Mask to Others */ - DHCPOPT_MASK_SUPPLIER = 30, - /** Perform Router Discovery */ - DHCPOPT_ROUTER_DISCOVERY = 31, - /** Router Solicitation Address */ - DHCPOPT_ROUTER_SOLICITATION_ADDRESS = 32, - /** Static Routing Table */ - DHCPOPT_STATIC_ROUTES = 33, - /** Trailer Encapsulation */ - DHCPOPT_TRAILER_ENCAPSULATION = 34, - /** ARP Cache Timeout */ - DHCPOPT_ARP_CACHE_TIMEOUT = 35, - /** IEEE802.3 Encapsulation */ - DHCPOPT_IEEE802_3_ENCAPSULATION = 36, - /** Default TCP Time to Live */ - DHCPOPT_DEFAULT_TCP_TTL = 37, - /** TCP Keepalive Interval */ - DHCPOPT_TCP_KEEPALIVE_INTERVAL = 38, - /** TCP Keepalive Garbage */ - DHCPOPT_TCP_KEEPALIVE_GARBAGE = 39, - /** NIS Domain Name */ - DHCPOPT_NIS_DOMAIN = 40, - /** NIS Server Addresses */ - DHCPOPT_NIS_SERVERS = 41, - /** NTP Server Addresses */ - DHCPOPT_NTP_SERVERS = 42, - /** Vendor Specific Information */ - DHCPOPT_VENDOR_ENCAPSULATED_OPTIONS = 43, - /** NETBIOS Name Servers */ - DHCPOPT_NETBIOS_NAME_SERVERS = 44, - /** NETBIOS Datagram Distribution */ - DHCPOPT_NETBIOS_DD_SERVER = 45, - /** NETBIOS Node Type */ - DHCPOPT_NETBIOS_NODE_TYPE = 46, - /** NETBIOS Scope */ - DHCPOPT_NETBIOS_SCOPE = 47, - /** X Window Font Server */ - DHCPOPT_FONT_SERVERS = 48, - /** X Window Display Manager */ - DHCPOPT_X_DISPLAY_MANAGER = 49, - /** Requested IP Address */ - DHCPOPT_DHCP_REQUESTED_ADDRESS = 50, - /** IP Address Lease Time */ - DHCPOPT_DHCP_LEASE_TIME = 51, - /** Overload "sname" or "file" */ - DHCPOPT_DHCP_OPTION_OVERLOAD = 52, - /** DHCP Message Type */ - DHCPOPT_DHCP_MESSAGE_TYPE = 53, - /** DHCP Server Identification */ - DHCPOPT_DHCP_SERVER_IDENTIFIER = 54, - /** Parameter Request List */ - DHCPOPT_DHCP_PARAMETER_REQUEST_LIST = 55, - /** DHCP Error Message */ - DHCPOPT_DHCP_MESSAGE = 56, - /** DHCP Maximum Message Size */ - DHCPOPT_DHCP_MAX_MESSAGE_SIZE = 57, - /** DHCP Renewal (T1) Time */ - DHCPOPT_DHCP_RENEWAL_TIME = 58, - /** DHCP Rebinding (T2) Time */ - DHCPOPT_DHCP_REBINDING_TIME = 59, - /** Class Identifier */ - DHCPOPT_VENDOR_CLASS_IDENTIFIER = 60, - /** Class Identifier */ - DHCPOPT_DHCP_CLIENT_IDENTIFIER = 61, - /** NetWare/IP Domain Name */ - DHCPOPT_NWIP_DOMAIN_NAME = 62, - /** NetWare/IP sub Options */ - DHCPOPT_NWIP_SUBOPTIONS = 63, - /** NIS+ v3 Client Domain Name */ - DHCPOPT_NIS_DOMAIN_NAME = 64, - /** NIS+ v3 Server Addresses */ - DHCPOPT_NIS_SERVER_ADDRESS = 65, - /** TFTP Server Name */ - DHCPOPT_TFTP_SERVER_NAME = 66, - /** Boot File Name */ - DHCPOPT_BOOTFILE_NAME = 67, - /** Home Agent Addresses */ - DHCPOPT_HOME_AGENT_ADDRESS = 68, - /** Simple Mail Server (SMTP) Addresses */ - DHCPOPT_SMTP_SERVER = 69, - /** Post Office (POP3) Server Addresses */ - DHCPOPT_POP3_SERVER = 70, - /** Network News (NNTP) Server Addresses */ - DHCPOPT_NNTP_SERVER = 71, - /** WWW Server Addresses */ - DHCPOPT_WWW_SERVER = 72, - /** Finger Server Addresses */ - DHCPOPT_FINGER_SERVER = 73, - /** Chat (IRC) Server Addresses */ - DHCPOPT_IRC_SERVER = 74, - /** StreetTalk Server Addresses */ - DHCPOPT_STREETTALK_SERVER = 75, - /** ST Directory Assist. Addresses */ - DHCPOPT_STDA_SERVER = 76, - /** User Class Information */ - DHCPOPT_USER_CLASS = 77, - /** Directory Agent Information */ - DHCPOPT_DIRECTORY_AGENT = 78, - /** Service Location Agent Scope */ - DHCPOPT_SERVICE_SCOPE = 79, - /** Rapid Commit */ - DHCPOPT_RAPID_COMMIT = 80, - /** Fully Qualified Domain Name */ - DHCPOPT_FQDN = 81, - /** Relay Agent Information */ - DHCPOPT_DHCP_AGENT_OPTIONS = 82, - /** Internet Storage Name Service */ - DHCPOPT_ISNS = 83, - /** Novell Directory Services */ - DHCPOPT_NDS_SERVERS = 85, - /** Novell Directory Services */ - DHCPOPT_NDS_TREE_NAME = 86, - /** Novell Directory Services */ - DHCPOPT_NDS_CONTEXT = 87, - /** BCMCS Controller Domain Name list */ - DHCPOPT_BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 88, - /** BCMCS Controller IPv4 address option */ - DHCPOPT_BCMCS_CONTROLLER_IPV4_ADDRESS = 89, - /** Authentication */ - DHCPOPT_AUTHENTICATION = 90, - /** Client Last Transaction Time */ - DHCPOPT_CLIENT_LAST_TXN_TIME = 91, - /** Associated IP */ - DHCPOPT_ASSOCIATED_IP = 92, - /** Client System Architecture */ - DHCPOPT_CLIENT_SYSTEM = 93, - /** Client Network Device Interface */ - DHCPOPT_CLIENT_NDI = 94, - /** Lightweight Directory Access Protocol [ */ - DHCPOPT_LDAP = 95, - /** UUID/GUID-based Client Identifier */ - DHCPOPT_UUID_GUID = 97, - /** Open Group's User Authentication */ - DHCPOPT_USER_AUTH = 98, - /** GEOCONF_CIVIC */ - DHCPOPT_GEOCONF_CIVIC = 99, - /** IEEE 1003.1 TZ String */ - DHCPOPT_PCODE = 100, - /** Reference to the TZ Database */ - DHCPOPT_TCODE = 101, - /** NetInfo Parent Server Address */ - DHCPOPT_NETINFO_ADDRESS = 112, - /** NetInfo Parent Server Tag */ - DHCPOPT_NETINFO_TAG = 113, - /** URL */ - DHCPOPT_URL = 114, - /** DHCP Auto-Configuration */ - DHCPOPT_AUTO_CONFIG = 116, - /** Name Service Search */ - DHCPOPT_NAME_SERVICE_SEARCH = 117, - /** Subnet Selection Option */ - DHCPOPT_SUBNET_SELECTION = 118, - /** DNS Domain Search List */ - DHCPOPT_DOMAIN_SEARCH = 119, - /** SIP Servers DHCP Option */ - DHCPOPT_SIP_SERVERS = 120, - /** Classless Static Route Option */ - DHCPOPT_CLASSLESS_STATIC_ROUTE = 121, - /** CableLabs Client Configuration */ - DHCPOPT_CCC = 122, - /** GeoConf Option */ - DHCPOPT_GEOCONF = 123, - /** Vendor-Identifying Vendor Class */ - DHCPOPT_V_I_VENDOR_CLASS = 124, - /** Vendor-Identifying Vendor-Specific Information */ - DHCPOPT_V_I_VENDOR_OPTS = 125, - /** OPTION_PANA_AGENT */ - DHCPOPT_OPTION_PANA_AGENT = 136, - /** OPTION_V4_LOST */ - DHCPOPT_OPTION_V4_LOST = 137, - /** CAPWAP Access Controller addresses */ - DHCPOPT_OPTION_CAPWAP_AC_V4 = 138, - /** A Series Of Suboptions */ - DHCPOPT_OPTION_IPV4_ADDRESS_MOS = 139, - /** A Series Of Suboptions */ - DHCPOPT_OPTION_IPV4_FQDN_MOS = 140, - /** List of domain names to search for SIP User Agent Configuration */ - DHCPOPT_SIP_UA_CONFIG = 141, - /** ANDSF IPv4 Address Option for DHCPv4 */ - DHCPOPT_OPTION_IPV4_ADDRESS_ANDSF = 142, - /** Geospatial Location with Uncertainty [RF */ - DHCPOPT_GEOLOC = 144, - /** Forcerenew Nonce Capable */ - DHCPOPT_FORCERENEW_NONCE_CAPABLE = 145, - /** Information for selecting RDNSS */ - DHCPOPT_RDNSS_SELECTION = 146, - /** Status code and optional N byte text message describing status */ - DHCPOPT_STATUS_CODE = 151, - /** Absolute time (seconds since Jan 1, 1970) message was sent */ - DHCPOPT_BASE_TIME = 152, - /** Number of seconds in the past when client entered current state */ - DHCPOPT_START_TIME_OF_STATE = 153, - /** Absolute time (seconds since Jan 1, 1970) for beginning of query */ - DHCPOPT_QUERY_START_TIME = 154, - /** Absolute time (seconds since Jan 1, 1970) for end of query */ - DHCPOPT_QUERY_END_TIME = 155, - /** State of IP address */ - DHCPOPT_DHCP_STATE = 156, - /** Indicates information came from local or remote server */ - DHCPOPT_DATA_SOURCE = 157, - /** Includes one or multiple lists of PCP server IP addresses; each list is treated as a separate PCP server */ - DHCPOPT_OPTION_V4_PCP_SERVER = 158, - /** This option is used to configure a set of ports bound to a shared IPv4 address */ - DHCPOPT_OPTION_V4_PORTPARAMS = 159, - /** DHCP Captive-Portal */ - DHCPOPT_CAPTIVE_PORTAL = 160, - /** Manufacturer Usage Descriptions */ - DHCPOPT_OPTION_MUD_URL_V4 = 161, - /** Etherboot */ - DHCPOPT_ETHERBOOT = 175, - /** IP Telephone */ - DHCPOPT_IP_TELEPHONE = 176, - /** Magic string = F1:00:74:7E */ - DHCPOPT_PXELINUX_MAGIC = 208, - /** Configuration file */ - DHCPOPT_CONFIGURATION_FILE = 209, - /** Path Prefix Option */ - DHCPOPT_PATH_PREFIX = 210, - /** Reboot Time */ - DHCPOPT_REBOOT_TIME = 211, - /** OPTION_6RD with N/4 6rd BR addresses */ - DHCPOPT_OPTION_6RD = 212, - /** Access Network Domain Name */ - DHCPOPT_OPTION_V4_ACCESS_DOMAIN = 213, - /** Subnet Allocation Option */ - DHCPOPT_SUBNET_ALLOCATION = 220, - /** Virtual Subnet Selection (VSS) Option */ - DHCPOPT_VIRTUAL_SUBNET_SELECTION = 221, - /** End (last option) */ - DHCPOPT_END = 255 - }; - - - /** - * @class DhcpOption - * A wrapper class for DHCP options. This class does not create or modify DHCP option records, but rather - * serves as a wrapper and provides useful methods for setting and retrieving data to/from them - */ - class DhcpOption : public TLVRecord - { - public: - - /** - * A c'tor for this class that gets a pointer to the option raw data (byte array) - * @param[in] optionRawData A pointer to the option raw data - */ - explicit DhcpOption(uint8_t* optionRawData) : TLVRecord(optionRawData) { } - - /** - * A d'tor for this class, currently does nothing - */ - virtual ~DhcpOption() { } - - /** - * Retrieve DHCP option data as IPv4 address. Relevant only if option value is indeed an IPv4 address - * @return DHCP option data as IPv4 address - */ - IPv4Address getValueAsIpAddr() const - { - return getValueAs(); - } - - /** - * Set DHCP option data as IPv4 address. This method copies the 4 bytes of the IP address to the option value - * @param[in] addr The IPv4 address to set - * @param[in] valueOffset An optional parameter that specifies where to start set the option data (default set to 0). For example: - * if option data is 20 bytes long and you want to set the IP address in the 4 last bytes then use this method like this: - * setValueIpAddr(your_addr, 16) - */ - void setValueIpAddr(const IPv4Address& addr, int valueOffset = 0) - { - setValue(addr.toInt(), valueOffset); - } - - /** - * Retrieve DHCP option data as string. Relevant only if option value is indeed a string - * @param[in] valueOffset An optional parameter that specifies where to start copy the DHCP option data. For example: - * when retrieving Client FQDN option, you may ignore the flags and RCODE fields using this method like this: - * getValueAsString(3). The default is 0 - start copying from the beginning of option data - * @return DHCP option data as string - */ - std::string getValueAsString(int valueOffset = 0) const - { - if (m_Data->recordLen - valueOffset < 1) - return ""; - - return std::string((const char*)m_Data->recordValue + valueOffset, (int)m_Data->recordLen - valueOffset); - } - - /** - * Set DHCP option data as string. This method copies the string to the option value. If the string is longer than option length - * the string is trimmed so it will fit the option length - * @param[in] stringValue The string to set - * @param[in] valueOffset An optional parameter that specifies where to start set the option data (default set to 0). For example: - * if option data is 20 bytes long and you want to set a 6 char-long string in the 6 last bytes then use this method like this: - * setValueString("string", 14) - */ - void setValueString(const std::string& stringValue, int valueOffset = 0) - { - // calculate the maximum length of the destination buffer - size_t len = (size_t)m_Data->recordLen - (size_t)valueOffset; - - // use the length of input string if a buffer is large enough for whole string - if (stringValue.length() < len) - len = stringValue.length(); - - memcpy(m_Data->recordValue + valueOffset, stringValue.data(), len); - } - - - // implement abstract methods - - size_t getTotalSize() const - { - if (m_Data->recordType == (uint8_t)DHCPOPT_END || m_Data->recordType == (uint8_t)DHCPOPT_PAD) - return sizeof(uint8_t); - - return sizeof(uint8_t) * 2 + (size_t)m_Data->recordLen; - } - - size_t getDataSize() const - { - if (m_Data->recordType == (uint8_t)DHCPOPT_END || m_Data->recordType == (uint8_t)DHCPOPT_PAD) - return 0; - - return m_Data->recordLen; - } - }; - - - /** - * @class DhcpOptionBuilder - * A class for building DHCP options. This builder receives the option parameters in its c'tor, - * builds the DHCP option raw buffer and provides a build() method to get a DhcpOption object out of it - */ - class DhcpOptionBuilder : public TLVRecordBuilder - { - public: - - /** - * A c'tor for building DHCP options which their value is a byte array. The DhcpOption object can later - * be retrieved by calling build() - * @param[in] optionType DHCP option type - * @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in any way - * @param[in] optionValueLen DHCP option value length in bytes - */ - DhcpOptionBuilder(DhcpOptionTypes optionType, const uint8_t* optionValue, uint8_t optionValueLen) : - TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) { } - - /** - * A c'tor for building DHCP options which have a 1-byte value. The DhcpOption object can later be retrieved - * by calling build() - * @param[in] optionType DHCP option type - * @param[in] optionValue A 1-byte option value - */ - DhcpOptionBuilder(DhcpOptionTypes optionType, uint8_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) { } - - /** - * A c'tor for building DHCP options which have a 2-byte value. The DhcpOption object can later be retrieved - * by calling build() - * @param[in] optionType DHCP option type - * @param[in] optionValue A 2-byte option value - */ - DhcpOptionBuilder(DhcpOptionTypes optionType, uint16_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) { } - - /** - * A c'tor for building DHCP options which have a 4-byte value. The DhcpOption object can later be retrieved - * by calling build() - * @param[in] optionType DHCP option type - * @param[in] optionValue A 4-byte option value - */ - DhcpOptionBuilder(DhcpOptionTypes optionType, uint32_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) { } - - /** - * A c'tor for building DHCP options which have an IPv4Address value. The DhcpOption object can later be - * retrieved by calling build() - * @param[in] optionType DHCP option type - * @param[in] optionValue The IPv4 address option value - */ - DhcpOptionBuilder(DhcpOptionTypes optionType, const IPv4Address& optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) { } - - /** - * A c'tor for building DHCP options which have a string value. The DhcpOption object can later be retrieved - * by calling build() - * @param[in] optionType DHCP option type - * @param[in] optionValue The string option value - */ - DhcpOptionBuilder(DhcpOptionTypes optionType, const std::string& optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) { } - - /** - * A copy c'tor which copies all the data from another instance of DhcpOptionBuilder - * @param[in] other The instance to copy from - */ - DhcpOptionBuilder(const DhcpOptionBuilder& other) : - TLVRecordBuilder(other) { } - - /** - * Assignment operator that copies all data from another instance of DhcpOptionBuilder - * @param[in] other The instance to assign from - * @return A reference to the assignee - */ - DhcpOptionBuilder& operator=(const DhcpOptionBuilder& other) - { - TLVRecordBuilder::operator=(other); - return *this; - } - - /** - * Build the DhcpOption object out of the parameters defined in the c'tor - * @return The DhcpOption object - */ - DhcpOption build() const; - }; - - - - /** - * @class DhcpLayer - * Represents a DHCP (Dynamic Host Configuration Protocol) protocol layer - */ - class DhcpLayer : public Layer - { - public: - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - DhcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that creates the layer from scratch. Adds a ::DHCPOPT_DHCP_MESSAGE_TYPE and a ::DHCPOPT_END - * options - * @param[in] msgType A DHCP message type to be set - * @param[in] clientMacAddr A client MAC address to set in dhcp_header#clientHardwareAddress field - */ - DhcpLayer(DhcpMessageType msgType, const MacAddress& clientMacAddr); - - /** - * A constructor that creates the layer from scratch with clean data - */ - DhcpLayer(); - - /** - * A destructor for this layer - */ - virtual ~DhcpLayer() {} - - /** - * Get a pointer to the DHCP header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref dhcp_header - */ - dhcp_header* getDhcpHeader() const { return (dhcp_header*)m_Data; } - - /** - * @return The BootP opcode of this message - */ - BootpOpCodes getOpCode() const { return (BootpOpCodes)getDhcpHeader()->opCode; } - - /** - * @return The client IPv4 address (as extracted from dhcp_header#clientIpAddress converted to IPv4Address object) - */ - IPv4Address getClientIpAddress() const { return getDhcpHeader()->clientIpAddress; } - - /** - * Set the client IPv4 address in dhcp_header#clientIpAddress - * @param[in] addr The IPv4 address to set - */ - void setClientIpAddress(const IPv4Address& addr) { getDhcpHeader()->clientIpAddress = addr.toInt(); } - - /** - * @return The server IPv4 address (as extracted from dhcp_header#serverIpAddress converted to IPv4Address object) - */ - IPv4Address getServerIpAddress() const { return getDhcpHeader()->serverIpAddress; } - - /** - * Set the server IPv4 address in dhcp_header#serverIpAddress - * @param[in] addr The IPv4 address to set - */ - void setServerIpAddress(const IPv4Address& addr) { getDhcpHeader()->serverIpAddress = addr.toInt(); } - - /** - * @return Your IPv4 address (as extracted from dhcp_header#yourIpAddress converted to IPv4Address object) - */ - IPv4Address getYourIpAddress() const { return getDhcpHeader()->yourIpAddress; } - - /** - * Set your IPv4 address in dhcp_header#yourIpAddress - * @param[in] addr The IPv4 address to set - */ - void setYourIpAddress(const IPv4Address& addr) { getDhcpHeader()->yourIpAddress = addr.toInt(); } - - /** - * @return Gateway IPv4 address (as extracted from dhcp_header#gatewayIpAddress converted to IPv4Address object) - */ - IPv4Address getGatewayIpAddress() const { return getDhcpHeader()->gatewayIpAddress; } - - /** - * Set the gateway IPv4 address in dhcp_header#gatewayIpAddress - * @param[in] addr The IPv4 address to set - */ - void setGatewayIpAddress(const IPv4Address& addr) { getDhcpHeader()->gatewayIpAddress = addr.toInt(); } - - /** - * @return The client MAC address as extracted from dhcp_header#clientHardwareAddress, assuming dhcp_header#hardwareType is 1 (Ethernet) - * and dhcp_header#hardwareAddressLength is 6 (MAC address length). Otherwise returns MacAddress#Zero - */ - MacAddress getClientHardwareAddress() const; - - /** - * Set a MAC address into the first 6 bytes of dhcp_header#clientHardwareAddress. This method also sets dhcp_header#hardwareType - * to 1 (Ethernet) and dhcp_header#hardwareAddressLength to 6 (MAC address length) - * @param[in] addr The MAC address to set - */ - void setClientHardwareAddress(const MacAddress& addr); - - /** - * @deprecated Deprecated due to typo. Please use getMessageType() - */ - PCPP_DEPRECATED DhcpMessageType getMesageType() const { return getMessageType(); }; - - /** - * @return DHCP message type as extracted from ::DHCPOPT_DHCP_MESSAGE_TYPE option. If this option doesn't exist the value of - * ::DHCP_UNKNOWN_MSG_TYPE is returned - */ - DhcpMessageType getMessageType() const; - - /** - * @deprecated Deprecated due to typo. Please use setMessageType() - */ - bool setMesageType(DhcpMessageType msgType) { return setMessageType(msgType); }; - - /** - * Set DHCP message type. This method searches for existing ::DHCPOPT_DHCP_MESSAGE_TYPE option. If found, it sets the requested - * message type as its value. If not, it creates a ::DHCPOPT_DHCP_MESSAGE_TYPE option and sets the requested message type as its - * value - * @param[in] msgType Message type to set - * @return True if message type was set successfully or false if msgType is ::DHCP_UNKNOWN_MSG_TYPE or if failed to add - * ::DHCPOPT_DHCP_MESSAGE_TYPE option - */ - bool setMessageType(DhcpMessageType msgType); - - /** - * @return The first DHCP option in the packet. If there are no DHCP options the returned value will contain - * a logical NULL (DhcpOption#isNull() == true) - */ - DhcpOption getFirstOptionData() const; - - /** - * Get the DHCP option that comes after a given option. If the given option was the last one, the - * returned value will contain a logical NULL (DhcpOption#isNull() == true) - * @param[in] dhcpOption A given DHCP option - * @return A DhcpOption object containing the option data that comes next, or logical NULL if the given DHCP - * option: (1) was the last one; (2) contains a logical NULL or (3) doesn't belong to this packet - */ - DhcpOption getNextOptionData(DhcpOption dhcpOption) const; - - /** - * Get a DHCP option by type - * @param[in] option DHCP option type - * @return A DhcpOption object containing the first DHCP option data that matches this type, or logical NULL - * (DhcpOption#isNull() == true) if no such option found - */ - DhcpOption getOptionData(DhcpOptionTypes option) const; - - /** - * @return The number of DHCP options in this layer - */ - size_t getOptionsCount() const; - - /** - * Add a new DHCP option at the end of the layer - * @param[in] optionBuilder A DhcpOptionBuilder object that contains the requested DHCP option data to add - * @return A DhcpOption object containing the newly added DHCP option data or logical NULL - * (DhcpOption#isNull() == true) if addition failed - */ - DhcpOption addOption(const DhcpOptionBuilder& optionBuilder); - - /** - * Add a new DHCP option after an existing one - * @param[in] optionBuilder A DhcpOptionBuilder object that contains the requested DHCP option data to add - * @param[in] prevOption The DHCP option type which the newly added option will come after - * @return A DhcpOption object containing the newly added DHCP option data or logical NULL - * (DhcpOption#isNull() == true) if addition failed - */ - DhcpOption addOptionAfter(const DhcpOptionBuilder& optionBuilder, DhcpOptionTypes prevOption); - - /** - * Remove an existing DHCP option from the layer - * @param[in] optionType The DHCP option type to remove - * @return True if DHCP option was successfully removed or false if type wasn't found or if removal failed - */ - bool removeOption(DhcpOptionTypes optionType); - - /** - * Remove all DHCP options in this layer - * @return True if all DHCP options were successfully removed or false if removal failed for some reason - */ - bool removeAllOptions(); - - // implement abstract methods - - /** - * Does nothing for this layer (DhcpLayer is always last) - */ - void parseNextLayer() {} - - /** - * @return The size of @ref dhcp_header + size of options - */ - size_t getHeaderLen() const { return m_DataLen; } - - /** - * Calculate the following fields: - * - @ref dhcp_header#magicNumber = DHCP magic number (0x63538263) - * - @ref dhcp_header#opCode = ::DHCP_BOOTREQUEST for message types: ::DHCP_DISCOVER, ::DHCP_REQUEST, ::DHCP_DECLINE, ::DHCP_RELEASE, - * ::DHCP_INFORM, ::DHCP_UNKNOWN_MSG_TYPE - * ::DHCP_BOOTREPLY for message types: ::DHCP_OFFER, ::DHCP_ACK, ::DHCP_NAK - * - @ref dhcp_header#hardwareType = 1 (Ethernet) - * - @ref dhcp_header#hardwareAddressLength = 6 (MAC address length) - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - - private: - - uint8_t* getOptionsBasePtr() const { return m_Data + sizeof(dhcp_header); } - - TLVRecordReader m_OptionReader; - - void initDhcpLayer(size_t numOfBytesToAllocate); - - DhcpOption addOptionAt(const DhcpOptionBuilder& optionBuilder, int offset); - }; -} - -#endif /* PACKETPP_DHCP_LAYER */ +#ifndef PACKETPP_DHCP_LAYER +#define PACKETPP_DHCP_LAYER + +#include "Layer.h" +#include "TLVData.h" +#include "IpAddress.h" +#include "MacAddress.h" +#include + +#ifndef PCPP_DEPRECATED +#if defined(__GNUC__) || defined(__clang__) +#define PCPP_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define PCPP_DEPRECATED __declspec(deprecated) +#else +#pragma message("WARNING: DEPRECATED feature is not implemented for this compiler") +#define PCPP_DEPRECATED +#endif +#endif + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + + /** + * @struct dhcp_header + * Represents a DHCP protocol header + */ + #pragma pack(push, 1) + struct dhcp_header + { + /** BootP opcode */ + uint8_t opCode; + /** Hardware type, set to 1 (Ethernet) by default */ + uint8_t hardwareType; + /** Hardware address length, set to 6 (MAC address length) by default */ + uint8_t hardwareAddressLength; + /** Hop count */ + uint8_t hops; + /** DHCP/BootP transaction ID */ + uint32_t transactionID; + /** The elapsed time, in seconds since the client sent its first BOOTREQUEST message */ + uint16_t secondsElapsed; + /** BootP flags */ + uint16_t flags; + /** Client IPv4 address */ + uint32_t clientIpAddress; + /** Your IPv4 address */ + uint32_t yourIpAddress; + /** Server IPv4 address */ + uint32_t serverIpAddress; + /** Gateway IPv4 address */ + uint32_t gatewayIpAddress; + /** Client hardware address, by default contains the MAC address (only 6 first bytes are used) */ + uint8_t clientHardwareAddress[16]; + /** BootP server name */ + uint8_t serverName[64]; + /** BootP boot file name */ + uint8_t bootFilename[128]; + /** DHCP magic number (set to the default value of 0x63538263) */ + uint32_t magicNumber; + }; + #pragma pack(pop) + + + /** + * BootP opcodes + */ + enum BootpOpCodes + { + /** BootP request */ + DHCP_BOOTREQUEST = 1, + /** BootP reply */ + DHCP_BOOTREPLY = 2 + }; + + /** + * DHCP message types + */ + enum DhcpMessageType + { + /** Unknown message type */ + DHCP_UNKNOWN_MSG_TYPE = 0, + /** Discover message type */ + DHCP_DISCOVER = 1, + /** Offer message type */ + DHCP_OFFER = 2, + /** Request message type */ + DHCP_REQUEST = 3, + /** Decline message type */ + DHCP_DECLINE = 4, + /** Acknowledge message type */ + DHCP_ACK = 5, + /** Non-acknowledge message type */ + DHCP_NAK = 6, + /** Release message type */ + DHCP_RELEASE = 7, + /** Inform message type */ + DHCP_INFORM = 8 + }; + + /** + * DHCP option types. + */ + enum DhcpOptionTypes + { + /** Unknown option type */ + DHCPOPT_UNKNOWN = -1, + /** Pad */ + DHCPOPT_PAD = 0, + /** Subnet Mask Value */ + DHCPOPT_SUBNET_MASK = 1, + /** Time Offset in Seconds from UTC */ + DHCPOPT_TIME_OFFSET = 2, + /** N/4 Router addresses */ + DHCPOPT_ROUTERS = 3, + /** N/4 Timeserver addresses */ + DHCPOPT_TIME_SERVERS = 4, + /** N/4 IEN-116 Server addresses */ + DHCPOPT_NAME_SERVERS = 5, + /** N/4 DNS Server addresses */ + DHCPOPT_DOMAIN_NAME_SERVERS = 6, + /** N/4 Logging Server addresses */ + DHCPOPT_LOG_SERVERS = 7, + /** N/4 Quotes Server addresses */ + DHCPOPT_QUOTES_SERVERS = 8, + /** N/4 Quotes Server addresses */ + DHCPOPT_LPR_SERVERS = 9, + /** N/4 Quotes Server addresses */ + DHCPOPT_IMPRESS_SERVERS = 10, + /** N/4 RLP Server addresses */ + DHCPOPT_RESOURCE_LOCATION_SERVERS = 11, + /** Hostname string */ + DHCPOPT_HOST_NAME = 12, + /** Size of boot file in 512 byte chunks */ + DHCPOPT_BOOT_SIZE = 13, + /** Client to dump and name the file to dump it to */ + DHCPOPT_MERIT_DUMP = 14, + /** The DNS domain name of the client */ + DHCPOPT_DOMAIN_NAME = 15, + /** Swap Server address */ + DHCPOPT_SWAP_SERVER = 16, + /** Path name for root disk */ + DHCPOPT_ROOT_PATH = 17, + /** Path name for more BOOTP info */ + DHCPOPT_EXTENSIONS_PATH = 18, + /** Enable/Disable IP Forwarding */ + DHCPOPT_IP_FORWARDING = 19, + /** Enable/Disable Source Routing */ + DHCPOPT_NON_LOCAL_SOURCE_ROUTING = 20, + /** Routing Policy Filters */ + DHCPOPT_POLICY_FILTER = 21, + /** Max Datagram Reassembly Size */ + DHCPOPT_MAX_DGRAM_REASSEMBLY = 22, + /** Default IP Time to Live */ + DEFAULT_IP_TTL = 23, + /** Path MTU Aging Timeout */ + DHCPOPT_PATH_MTU_AGING_TIMEOUT = 24, + /** Path MTU Plateau Table */ + PATH_MTU_PLATEAU_TABLE = 25, + /** Interface MTU Size */ + DHCPOPT_INTERFACE_MTU = 26, + /** All Subnets are Local */ + DHCPOPT_ALL_SUBNETS_LOCAL = 27, + /** Broadcast Address */ + DHCPOPT_BROADCAST_ADDRESS = 28, + /** Perform Mask Discovery */ + DHCPOPT_PERFORM_MASK_DISCOVERY = 29, + /** Provide Mask to Others */ + DHCPOPT_MASK_SUPPLIER = 30, + /** Perform Router Discovery */ + DHCPOPT_ROUTER_DISCOVERY = 31, + /** Router Solicitation Address */ + DHCPOPT_ROUTER_SOLICITATION_ADDRESS = 32, + /** Static Routing Table */ + DHCPOPT_STATIC_ROUTES = 33, + /** Trailer Encapsulation */ + DHCPOPT_TRAILER_ENCAPSULATION = 34, + /** ARP Cache Timeout */ + DHCPOPT_ARP_CACHE_TIMEOUT = 35, + /** IEEE802.3 Encapsulation */ + DHCPOPT_IEEE802_3_ENCAPSULATION = 36, + /** Default TCP Time to Live */ + DHCPOPT_DEFAULT_TCP_TTL = 37, + /** TCP Keepalive Interval */ + DHCPOPT_TCP_KEEPALIVE_INTERVAL = 38, + /** TCP Keepalive Garbage */ + DHCPOPT_TCP_KEEPALIVE_GARBAGE = 39, + /** NIS Domain Name */ + DHCPOPT_NIS_DOMAIN = 40, + /** NIS Server Addresses */ + DHCPOPT_NIS_SERVERS = 41, + /** NTP Server Addresses */ + DHCPOPT_NTP_SERVERS = 42, + /** Vendor Specific Information */ + DHCPOPT_VENDOR_ENCAPSULATED_OPTIONS = 43, + /** NETBIOS Name Servers */ + DHCPOPT_NETBIOS_NAME_SERVERS = 44, + /** NETBIOS Datagram Distribution */ + DHCPOPT_NETBIOS_DD_SERVER = 45, + /** NETBIOS Node Type */ + DHCPOPT_NETBIOS_NODE_TYPE = 46, + /** NETBIOS Scope */ + DHCPOPT_NETBIOS_SCOPE = 47, + /** X Window Font Server */ + DHCPOPT_FONT_SERVERS = 48, + /** X Window Display Manager */ + DHCPOPT_X_DISPLAY_MANAGER = 49, + /** Requested IP Address */ + DHCPOPT_DHCP_REQUESTED_ADDRESS = 50, + /** IP Address Lease Time */ + DHCPOPT_DHCP_LEASE_TIME = 51, + /** Overload "sname" or "file" */ + DHCPOPT_DHCP_OPTION_OVERLOAD = 52, + /** DHCP Message Type */ + DHCPOPT_DHCP_MESSAGE_TYPE = 53, + /** DHCP Server Identification */ + DHCPOPT_DHCP_SERVER_IDENTIFIER = 54, + /** Parameter Request List */ + DHCPOPT_DHCP_PARAMETER_REQUEST_LIST = 55, + /** DHCP Error Message */ + DHCPOPT_DHCP_MESSAGE = 56, + /** DHCP Maximum Message Size */ + DHCPOPT_DHCP_MAX_MESSAGE_SIZE = 57, + /** DHCP Renewal (T1) Time */ + DHCPOPT_DHCP_RENEWAL_TIME = 58, + /** DHCP Rebinding (T2) Time */ + DHCPOPT_DHCP_REBINDING_TIME = 59, + /** Class Identifier */ + DHCPOPT_VENDOR_CLASS_IDENTIFIER = 60, + /** Class Identifier */ + DHCPOPT_DHCP_CLIENT_IDENTIFIER = 61, + /** NetWare/IP Domain Name */ + DHCPOPT_NWIP_DOMAIN_NAME = 62, + /** NetWare/IP sub Options */ + DHCPOPT_NWIP_SUBOPTIONS = 63, + /** NIS+ v3 Client Domain Name */ + DHCPOPT_NIS_DOMAIN_NAME = 64, + /** NIS+ v3 Server Addresses */ + DHCPOPT_NIS_SERVER_ADDRESS = 65, + /** TFTP Server Name */ + DHCPOPT_TFTP_SERVER_NAME = 66, + /** Boot File Name */ + DHCPOPT_BOOTFILE_NAME = 67, + /** Home Agent Addresses */ + DHCPOPT_HOME_AGENT_ADDRESS = 68, + /** Simple Mail Server (SMTP) Addresses */ + DHCPOPT_SMTP_SERVER = 69, + /** Post Office (POP3) Server Addresses */ + DHCPOPT_POP3_SERVER = 70, + /** Network News (NNTP) Server Addresses */ + DHCPOPT_NNTP_SERVER = 71, + /** WWW Server Addresses */ + DHCPOPT_WWW_SERVER = 72, + /** Finger Server Addresses */ + DHCPOPT_FINGER_SERVER = 73, + /** Chat (IRC) Server Addresses */ + DHCPOPT_IRC_SERVER = 74, + /** StreetTalk Server Addresses */ + DHCPOPT_STREETTALK_SERVER = 75, + /** ST Directory Assist. Addresses */ + DHCPOPT_STDA_SERVER = 76, + /** User Class Information */ + DHCPOPT_USER_CLASS = 77, + /** Directory Agent Information */ + DHCPOPT_DIRECTORY_AGENT = 78, + /** Service Location Agent Scope */ + DHCPOPT_SERVICE_SCOPE = 79, + /** Rapid Commit */ + DHCPOPT_RAPID_COMMIT = 80, + /** Fully Qualified Domain Name */ + DHCPOPT_FQDN = 81, + /** Relay Agent Information */ + DHCPOPT_DHCP_AGENT_OPTIONS = 82, + /** Internet Storage Name Service */ + DHCPOPT_ISNS = 83, + /** Novell Directory Services */ + DHCPOPT_NDS_SERVERS = 85, + /** Novell Directory Services */ + DHCPOPT_NDS_TREE_NAME = 86, + /** Novell Directory Services */ + DHCPOPT_NDS_CONTEXT = 87, + /** BCMCS Controller Domain Name list */ + DHCPOPT_BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 88, + /** BCMCS Controller IPv4 address option */ + DHCPOPT_BCMCS_CONTROLLER_IPV4_ADDRESS = 89, + /** Authentication */ + DHCPOPT_AUTHENTICATION = 90, + /** Client Last Transaction Time */ + DHCPOPT_CLIENT_LAST_TXN_TIME = 91, + /** Associated IP */ + DHCPOPT_ASSOCIATED_IP = 92, + /** Client System Architecture */ + DHCPOPT_CLIENT_SYSTEM = 93, + /** Client Network Device Interface */ + DHCPOPT_CLIENT_NDI = 94, + /** Lightweight Directory Access Protocol [ */ + DHCPOPT_LDAP = 95, + /** UUID/GUID-based Client Identifier */ + DHCPOPT_UUID_GUID = 97, + /** Open Group's User Authentication */ + DHCPOPT_USER_AUTH = 98, + /** GEOCONF_CIVIC */ + DHCPOPT_GEOCONF_CIVIC = 99, + /** IEEE 1003.1 TZ String */ + DHCPOPT_PCODE = 100, + /** Reference to the TZ Database */ + DHCPOPT_TCODE = 101, + /** NetInfo Parent Server Address */ + DHCPOPT_NETINFO_ADDRESS = 112, + /** NetInfo Parent Server Tag */ + DHCPOPT_NETINFO_TAG = 113, + /** URL */ + DHCPOPT_URL = 114, + /** DHCP Auto-Configuration */ + DHCPOPT_AUTO_CONFIG = 116, + /** Name Service Search */ + DHCPOPT_NAME_SERVICE_SEARCH = 117, + /** Subnet Selection Option */ + DHCPOPT_SUBNET_SELECTION = 118, + /** DNS Domain Search List */ + DHCPOPT_DOMAIN_SEARCH = 119, + /** SIP Servers DHCP Option */ + DHCPOPT_SIP_SERVERS = 120, + /** Classless Static Route Option */ + DHCPOPT_CLASSLESS_STATIC_ROUTE = 121, + /** CableLabs Client Configuration */ + DHCPOPT_CCC = 122, + /** GeoConf Option */ + DHCPOPT_GEOCONF = 123, + /** Vendor-Identifying Vendor Class */ + DHCPOPT_V_I_VENDOR_CLASS = 124, + /** Vendor-Identifying Vendor-Specific Information */ + DHCPOPT_V_I_VENDOR_OPTS = 125, + /** OPTION_PANA_AGENT */ + DHCPOPT_OPTION_PANA_AGENT = 136, + /** OPTION_V4_LOST */ + DHCPOPT_OPTION_V4_LOST = 137, + /** CAPWAP Access Controller addresses */ + DHCPOPT_OPTION_CAPWAP_AC_V4 = 138, + /** A Series Of Suboptions */ + DHCPOPT_OPTION_IPV4_ADDRESS_MOS = 139, + /** A Series Of Suboptions */ + DHCPOPT_OPTION_IPV4_FQDN_MOS = 140, + /** List of domain names to search for SIP User Agent Configuration */ + DHCPOPT_SIP_UA_CONFIG = 141, + /** ANDSF IPv4 Address Option for DHCPv4 */ + DHCPOPT_OPTION_IPV4_ADDRESS_ANDSF = 142, + /** Geospatial Location with Uncertainty [RF */ + DHCPOPT_GEOLOC = 144, + /** Forcerenew Nonce Capable */ + DHCPOPT_FORCERENEW_NONCE_CAPABLE = 145, + /** Information for selecting RDNSS */ + DHCPOPT_RDNSS_SELECTION = 146, + /** Status code and optional N byte text message describing status */ + DHCPOPT_STATUS_CODE = 151, + /** Absolute time (seconds since Jan 1, 1970) message was sent */ + DHCPOPT_BASE_TIME = 152, + /** Number of seconds in the past when client entered current state */ + DHCPOPT_START_TIME_OF_STATE = 153, + /** Absolute time (seconds since Jan 1, 1970) for beginning of query */ + DHCPOPT_QUERY_START_TIME = 154, + /** Absolute time (seconds since Jan 1, 1970) for end of query */ + DHCPOPT_QUERY_END_TIME = 155, + /** State of IP address */ + DHCPOPT_DHCP_STATE = 156, + /** Indicates information came from local or remote server */ + DHCPOPT_DATA_SOURCE = 157, + /** Includes one or multiple lists of PCP server IP addresses; each list is treated as a separate PCP server */ + DHCPOPT_OPTION_V4_PCP_SERVER = 158, + /** This option is used to configure a set of ports bound to a shared IPv4 address */ + DHCPOPT_OPTION_V4_PORTPARAMS = 159, + /** DHCP Captive-Portal */ + DHCPOPT_CAPTIVE_PORTAL = 160, + /** Manufacturer Usage Descriptions */ + DHCPOPT_OPTION_MUD_URL_V4 = 161, + /** Etherboot */ + DHCPOPT_ETHERBOOT = 175, + /** IP Telephone */ + DHCPOPT_IP_TELEPHONE = 176, + /** Magic string = F1:00:74:7E */ + DHCPOPT_PXELINUX_MAGIC = 208, + /** Configuration file */ + DHCPOPT_CONFIGURATION_FILE = 209, + /** Path Prefix Option */ + DHCPOPT_PATH_PREFIX = 210, + /** Reboot Time */ + DHCPOPT_REBOOT_TIME = 211, + /** OPTION_6RD with N/4 6rd BR addresses */ + DHCPOPT_OPTION_6RD = 212, + /** Access Network Domain Name */ + DHCPOPT_OPTION_V4_ACCESS_DOMAIN = 213, + /** Subnet Allocation Option */ + DHCPOPT_SUBNET_ALLOCATION = 220, + /** Virtual Subnet Selection (VSS) Option */ + DHCPOPT_VIRTUAL_SUBNET_SELECTION = 221, + /** End (last option) */ + DHCPOPT_END = 255 + }; + + + /** + * @class DhcpOption + * A wrapper class for DHCP options. This class does not create or modify DHCP option records, but rather + * serves as a wrapper and provides useful methods for setting and retrieving data to/from them + */ + class DhcpOption : public TLVRecord + { + public: + + /** + * A c'tor for this class that gets a pointer to the option raw data (byte array) + * @param[in] optionRawData A pointer to the option raw data + */ + explicit DhcpOption(uint8_t* optionRawData) : TLVRecord(optionRawData) { } + + /** + * A d'tor for this class, currently does nothing + */ + virtual ~DhcpOption() { } + + /** + * Retrieve DHCP option data as IPv4 address. Relevant only if option value is indeed an IPv4 address + * @return DHCP option data as IPv4 address + */ + IPv4Address getValueAsIpAddr() const + { + return getValueAs(); + } + + /** + * Set DHCP option data as IPv4 address. This method copies the 4 bytes of the IP address to the option value + * @param[in] addr The IPv4 address to set + * @param[in] valueOffset An optional parameter that specifies where to start set the option data (default set to 0). For example: + * if option data is 20 bytes long and you want to set the IP address in the 4 last bytes then use this method like this: + * setValueIpAddr(your_addr, 16) + */ + void setValueIpAddr(const IPv4Address& addr, int valueOffset = 0) + { + setValue(addr.toInt(), valueOffset); + } + + /** + * Retrieve DHCP option data as string. Relevant only if option value is indeed a string + * @param[in] valueOffset An optional parameter that specifies where to start copy the DHCP option data. For example: + * when retrieving Client FQDN option, you may ignore the flags and RCODE fields using this method like this: + * getValueAsString(3). The default is 0 - start copying from the beginning of option data + * @return DHCP option data as string + */ + std::string getValueAsString(int valueOffset = 0) const + { + if (m_Data->recordLen - valueOffset < 1) + return ""; + + return std::string((const char*)m_Data->recordValue + valueOffset, (int)m_Data->recordLen - valueOffset); + } + + /** + * Set DHCP option data as string. This method copies the string to the option value. If the string is longer than option length + * the string is trimmed so it will fit the option length + * @param[in] stringValue The string to set + * @param[in] valueOffset An optional parameter that specifies where to start set the option data (default set to 0). For example: + * if option data is 20 bytes long and you want to set a 6 char-long string in the 6 last bytes then use this method like this: + * setValueString("string", 14) + */ + void setValueString(const std::string& stringValue, int valueOffset = 0) + { + // calculate the maximum length of the destination buffer + size_t len = (size_t)m_Data->recordLen - (size_t)valueOffset; + + // use the length of input string if a buffer is large enough for whole string + if (stringValue.length() < len) + len = stringValue.length(); + + memcpy(m_Data->recordValue + valueOffset, stringValue.data(), len); + } + + + // implement abstract methods + + size_t getTotalSize() const + { + if (m_Data->recordType == (uint8_t)DHCPOPT_END || m_Data->recordType == (uint8_t)DHCPOPT_PAD) + return sizeof(uint8_t); + + return sizeof(uint8_t) * 2 + (size_t)m_Data->recordLen; + } + + size_t getDataSize() const + { + if (m_Data->recordType == (uint8_t)DHCPOPT_END || m_Data->recordType == (uint8_t)DHCPOPT_PAD) + return 0; + + return m_Data->recordLen; + } + }; + + + /** + * @class DhcpOptionBuilder + * A class for building DHCP options. This builder receives the option parameters in its c'tor, + * builds the DHCP option raw buffer and provides a build() method to get a DhcpOption object out of it + */ + class DhcpOptionBuilder : public TLVRecordBuilder + { + public: + + /** + * A c'tor for building DHCP options which their value is a byte array. The DhcpOption object can later + * be retrieved by calling build() + * @param[in] optionType DHCP option type + * @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in any way + * @param[in] optionValueLen DHCP option value length in bytes + */ + DhcpOptionBuilder(DhcpOptionTypes optionType, const uint8_t* optionValue, uint8_t optionValueLen) : + TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) { } + + /** + * A c'tor for building DHCP options which have a 1-byte value. The DhcpOption object can later be retrieved + * by calling build() + * @param[in] optionType DHCP option type + * @param[in] optionValue A 1-byte option value + */ + DhcpOptionBuilder(DhcpOptionTypes optionType, uint8_t optionValue) : + TLVRecordBuilder((uint8_t)optionType, optionValue) { } + + /** + * A c'tor for building DHCP options which have a 2-byte value. The DhcpOption object can later be retrieved + * by calling build() + * @param[in] optionType DHCP option type + * @param[in] optionValue A 2-byte option value + */ + DhcpOptionBuilder(DhcpOptionTypes optionType, uint16_t optionValue) : + TLVRecordBuilder((uint8_t)optionType, optionValue) { } + + /** + * A c'tor for building DHCP options which have a 4-byte value. The DhcpOption object can later be retrieved + * by calling build() + * @param[in] optionType DHCP option type + * @param[in] optionValue A 4-byte option value + */ + DhcpOptionBuilder(DhcpOptionTypes optionType, uint32_t optionValue) : + TLVRecordBuilder((uint8_t)optionType, optionValue) { } + + /** + * A c'tor for building DHCP options which have an IPv4Address value. The DhcpOption object can later be + * retrieved by calling build() + * @param[in] optionType DHCP option type + * @param[in] optionValue The IPv4 address option value + */ + DhcpOptionBuilder(DhcpOptionTypes optionType, const IPv4Address& optionValue) : + TLVRecordBuilder((uint8_t)optionType, optionValue) { } + + /** + * A c'tor for building DHCP options which have a string value. The DhcpOption object can later be retrieved + * by calling build() + * @param[in] optionType DHCP option type + * @param[in] optionValue The string option value + */ + DhcpOptionBuilder(DhcpOptionTypes optionType, const std::string& optionValue) : + TLVRecordBuilder((uint8_t)optionType, optionValue) { } + + /** + * A copy c'tor which copies all the data from another instance of DhcpOptionBuilder + * @param[in] other The instance to copy from + */ + DhcpOptionBuilder(const DhcpOptionBuilder& other) : + TLVRecordBuilder(other) { } + + /** + * Assignment operator that copies all data from another instance of DhcpOptionBuilder + * @param[in] other The instance to assign from + * @return A reference to the assignee + */ + DhcpOptionBuilder& operator=(const DhcpOptionBuilder& other) + { + TLVRecordBuilder::operator=(other); + return *this; + } + + /** + * Build the DhcpOption object out of the parameters defined in the c'tor + * @return The DhcpOption object + */ + DhcpOption build() const; + }; + + + + /** + * @class DhcpLayer + * Represents a DHCP (Dynamic Host Configuration Protocol) protocol layer + */ + class DhcpLayer : public Layer + { + public: + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + DhcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * A constructor that creates the layer from scratch. Adds a ::DHCPOPT_DHCP_MESSAGE_TYPE and a ::DHCPOPT_END + * options + * @param[in] msgType A DHCP message type to be set + * @param[in] clientMacAddr A client MAC address to set in dhcp_header#clientHardwareAddress field + */ + DhcpLayer(DhcpMessageType msgType, const MacAddress& clientMacAddr); + + /** + * A constructor that creates the layer from scratch with clean data + */ + DhcpLayer(); + + /** + * A destructor for this layer + */ + virtual ~DhcpLayer() {} + + /** + * Get a pointer to the DHCP header. Notice this points directly to the data, so every change will change the actual packet data + * @return A pointer to the @ref dhcp_header + */ + dhcp_header* getDhcpHeader() const { return (dhcp_header*)m_Data; } + + /** + * @return The BootP opcode of this message + */ + BootpOpCodes getOpCode() const { return (BootpOpCodes)getDhcpHeader()->opCode; } + + /** + * @return The client IPv4 address (as extracted from dhcp_header#clientIpAddress converted to IPv4Address object) + */ + IPv4Address getClientIpAddress() const { return getDhcpHeader()->clientIpAddress; } + + /** + * Set the client IPv4 address in dhcp_header#clientIpAddress + * @param[in] addr The IPv4 address to set + */ + void setClientIpAddress(const IPv4Address& addr) { getDhcpHeader()->clientIpAddress = addr.toInt(); } + + /** + * @return The server IPv4 address (as extracted from dhcp_header#serverIpAddress converted to IPv4Address object) + */ + IPv4Address getServerIpAddress() const { return getDhcpHeader()->serverIpAddress; } + + /** + * Set the server IPv4 address in dhcp_header#serverIpAddress + * @param[in] addr The IPv4 address to set + */ + void setServerIpAddress(const IPv4Address& addr) { getDhcpHeader()->serverIpAddress = addr.toInt(); } + + /** + * @return Your IPv4 address (as extracted from dhcp_header#yourIpAddress converted to IPv4Address object) + */ + IPv4Address getYourIpAddress() const { return getDhcpHeader()->yourIpAddress; } + + /** + * Set your IPv4 address in dhcp_header#yourIpAddress + * @param[in] addr The IPv4 address to set + */ + void setYourIpAddress(const IPv4Address& addr) { getDhcpHeader()->yourIpAddress = addr.toInt(); } + + /** + * @return Gateway IPv4 address (as extracted from dhcp_header#gatewayIpAddress converted to IPv4Address object) + */ + IPv4Address getGatewayIpAddress() const { return getDhcpHeader()->gatewayIpAddress; } + + /** + * Set the gateway IPv4 address in dhcp_header#gatewayIpAddress + * @param[in] addr The IPv4 address to set + */ + void setGatewayIpAddress(const IPv4Address& addr) { getDhcpHeader()->gatewayIpAddress = addr.toInt(); } + + /** + * @return The client MAC address as extracted from dhcp_header#clientHardwareAddress, assuming dhcp_header#hardwareType is 1 (Ethernet) + * and dhcp_header#hardwareAddressLength is 6 (MAC address length). Otherwise returns MacAddress#Zero + */ + MacAddress getClientHardwareAddress() const; + + /** + * Set a MAC address into the first 6 bytes of dhcp_header#clientHardwareAddress. This method also sets dhcp_header#hardwareType + * to 1 (Ethernet) and dhcp_header#hardwareAddressLength to 6 (MAC address length) + * @param[in] addr The MAC address to set + */ + void setClientHardwareAddress(const MacAddress& addr); + + /** + * @deprecated Deprecated due to typo. Please use getMessageType() + */ + PCPP_DEPRECATED DhcpMessageType getMesageType() const { return getMessageType(); }; + + /** + * @return DHCP message type as extracted from ::DHCPOPT_DHCP_MESSAGE_TYPE option. If this option doesn't exist the value of + * ::DHCP_UNKNOWN_MSG_TYPE is returned + */ + DhcpMessageType getMessageType() const; + + /** + * @deprecated Deprecated due to typo. Please use setMessageType() + */ + bool setMesageType(DhcpMessageType msgType) { return setMessageType(msgType); }; + + /** + * Set DHCP message type. This method searches for existing ::DHCPOPT_DHCP_MESSAGE_TYPE option. If found, it sets the requested + * message type as its value. If not, it creates a ::DHCPOPT_DHCP_MESSAGE_TYPE option and sets the requested message type as its + * value + * @param[in] msgType Message type to set + * @return True if message type was set successfully or false if msgType is ::DHCP_UNKNOWN_MSG_TYPE or if failed to add + * ::DHCPOPT_DHCP_MESSAGE_TYPE option + */ + bool setMessageType(DhcpMessageType msgType); + + /** + * @return The first DHCP option in the packet. If there are no DHCP options the returned value will contain + * a logical NULL (DhcpOption#isNull() == true) + */ + DhcpOption getFirstOptionData() const; + + /** + * Get the DHCP option that comes after a given option. If the given option was the last one, the + * returned value will contain a logical NULL (DhcpOption#isNull() == true) + * @param[in] dhcpOption A given DHCP option + * @return A DhcpOption object containing the option data that comes next, or logical NULL if the given DHCP + * option: (1) was the last one; (2) contains a logical NULL or (3) doesn't belong to this packet + */ + DhcpOption getNextOptionData(DhcpOption dhcpOption) const; + + /** + * Get a DHCP option by type + * @param[in] option DHCP option type + * @return A DhcpOption object containing the first DHCP option data that matches this type, or logical NULL + * (DhcpOption#isNull() == true) if no such option found + */ + DhcpOption getOptionData(DhcpOptionTypes option) const; + + /** + * @return The number of DHCP options in this layer + */ + size_t getOptionsCount() const; + + /** + * Add a new DHCP option at the end of the layer + * @param[in] optionBuilder A DhcpOptionBuilder object that contains the requested DHCP option data to add + * @return A DhcpOption object containing the newly added DHCP option data or logical NULL + * (DhcpOption#isNull() == true) if addition failed + */ + DhcpOption addOption(const DhcpOptionBuilder& optionBuilder); + + /** + * Add a new DHCP option after an existing one + * @param[in] optionBuilder A DhcpOptionBuilder object that contains the requested DHCP option data to add + * @param[in] prevOption The DHCP option type which the newly added option will come after + * @return A DhcpOption object containing the newly added DHCP option data or logical NULL + * (DhcpOption#isNull() == true) if addition failed + */ + DhcpOption addOptionAfter(const DhcpOptionBuilder& optionBuilder, DhcpOptionTypes prevOption); + + /** + * Remove an existing DHCP option from the layer + * @param[in] optionType The DHCP option type to remove + * @return True if DHCP option was successfully removed or false if type wasn't found or if removal failed + */ + bool removeOption(DhcpOptionTypes optionType); + + /** + * Remove all DHCP options in this layer + * @return True if all DHCP options were successfully removed or false if removal failed for some reason + */ + bool removeAllOptions(); + + // implement abstract methods + + /** + * Does nothing for this layer (DhcpLayer is always last) + */ + void parseNextLayer() {} + + /** + * @return The size of @ref dhcp_header + size of options + */ + size_t getHeaderLen() const { return m_DataLen; } + + /** + * Calculate the following fields: + * - @ref dhcp_header#magicNumber = DHCP magic number (0x63538263) + * - @ref dhcp_header#opCode = ::DHCP_BOOTREQUEST for message types: ::DHCP_DISCOVER, ::DHCP_REQUEST, ::DHCP_DECLINE, ::DHCP_RELEASE, + * ::DHCP_INFORM, ::DHCP_UNKNOWN_MSG_TYPE + * ::DHCP_BOOTREPLY for message types: ::DHCP_OFFER, ::DHCP_ACK, ::DHCP_NAK + * - @ref dhcp_header#hardwareType = 1 (Ethernet) + * - @ref dhcp_header#hardwareAddressLength = 6 (MAC address length) + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } + + private: + + uint8_t* getOptionsBasePtr() const { return m_Data + sizeof(dhcp_header); } + + TLVRecordReader m_OptionReader; + + void initDhcpLayer(size_t numOfBytesToAllocate); + + DhcpOption addOptionAt(const DhcpOptionBuilder& optionBuilder, int offset); + }; +} + +#endif /* PACKETPP_DHCP_LAYER */ diff --git a/Packet++/header/HttpLayer.h b/Packet++/header/HttpLayer.h index 8abb2dae35..62190de8dd 100644 --- a/Packet++/header/HttpLayer.h +++ b/Packet++/header/HttpLayer.h @@ -1,703 +1,703 @@ -#ifndef PACKETPP_HTTP_LAYER -#define PACKETPP_HTTP_LAYER - -#include "TextBasedProtocol.h" -#include -#include - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - - /** - * An enum for HTTP version - */ - enum HttpVersion - { - /** HTTP/0.9 */ - ZeroDotNine, - /** HTTP/1.0 */ - OneDotZero, - /** HTTP/1.1 */ - OneDotOne, - /** Unknown HTTP version */ - HttpVersionUnknown - }; - - // some popular HTTP fields - - /** Host field */ -#define PCPP_HTTP_HOST_FIELD "Host" - /** Connection field */ -#define PCPP_HTTP_CONNECTION_FIELD "Connection" - /** User-Agent field */ -#define PCPP_HTTP_USER_AGENT_FIELD "User-Agent" - /** Referer field */ -#define PCPP_HTTP_REFERER_FIELD "Referer" - /** Accept field */ -#define PCPP_HTTP_ACCEPT_FIELD "Accept" - /** Accept-Encoding field */ -#define PCPP_HTTP_ACCEPT_ENCODING_FIELD "Accept-Encoding" - /** Accept-Language field */ -#define PCPP_HTTP_ACCEPT_LANGUAGE_FIELD "Accept-Language" - /** Cookie field */ -#define PCPP_HTTP_COOKIE_FIELD "Cookie" - /** Content-Length field */ -#define PCPP_HTTP_CONTENT_LENGTH_FIELD "Content-Length" - /** Content-Encoding field */ -#define PCPP_HTTP_CONTENT_ENCODING_FIELD "Content-Encoding" - /** Content-Type field */ -#define PCPP_HTTP_CONTENT_TYPE_FIELD "Content-Type" - /** Transfer-Encoding field */ -#define PCPP_HTTP_TRANSFER_ENCODING_FIELD "Transfer-Encoding" - /** Server field */ -#define PCPP_HTTP_SERVER_FIELD "Server" - - - - // -------- Class HttpMessage ----------------- - - - /** - * @class HttpMessage - * Represents a general HTTP message. It's an abstract class and cannot be instantiated. It's inherited by HttpRequestLayer and HttpResponseLayer - */ - class HttpMessage : public TextBasedProtocolMessage - { - public: - - virtual ~HttpMessage() {} - - /** - * A static method that checks whether the port is considered as HTTP - * @param[in] port The port number to be checked - * @return True if the port matches those associated with the HTTP protocol - */ - static bool isHttpPort(uint16_t port) { return port == 80 || port == 8080; } - - // overridden methods - - virtual HeaderField* addField(const std::string& fieldName, const std::string& fieldValue); - virtual HeaderField* addField(const HeaderField& newField); - virtual HeaderField* insertField(HeaderField* prevField, const std::string& fieldName, const std::string& fieldValue); - virtual HeaderField* insertField(HeaderField* prevField, const HeaderField& newField); - - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - - protected: - HttpMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : TextBasedProtocolMessage(data, dataLen, prevLayer, packet) {} - HttpMessage() : TextBasedProtocolMessage() {} - HttpMessage(const HttpMessage& other) : TextBasedProtocolMessage(other) {} - HttpMessage& operator=(const HttpMessage& other) { TextBasedProtocolMessage::operator=(other); return *this; } - - // implementation of abstract methods - char getHeaderFieldNameValueSeparator() const { return ':'; } - bool spacesAllowedBetweenHeaderFieldNameAndValue() const { return true; } - }; - - - - - class HttpRequestFirstLine; - - - - // -------- Class HttpRequestLayer ----------------- - - /** - * @class HttpRequestLayer - * Represents an HTTP request header and inherits all basic functionality of HttpMessage and TextBasedProtocolMessage. - * The functionality that is added for this class is the HTTP first line concept. An HTTP request has the following first line: - * GET /bla/blabla.asp HTTP/1.1 - * Since it's not an "ordinary" HTTP field, it requires a special treatment and gets a class of it's own: HttpRequestFirstLine. - * Unlike most L2-4 protocols, an HTTP request header can spread over more than 1 packet. PcapPlusPlus currently doesn't support a header - * that is spread over more than 1 packet so in such cases: 1) only the first packet will be parsed as HttpRequestLayer (the other packets - * won't be recognized as HttpRequestLayer) and 2) the HTTP header for the first packet won't be complete (as it continues in the following - * packets), this why PcapPlusPlus can indicate that HTTP request header is complete or not(doesn't end with "\r\n\r\n" or "\n\n") using - * HttpMessage#isHeaderComplete() - */ - class HttpRequestLayer : public HttpMessage - { - friend class HttpRequestFirstLine; - public: - /** - * HTTP request methods - */ - enum HttpMethod - { - /** GET */ - HttpGET, - /** HEAD */ - HttpHEAD, - /** POST */ - HttpPOST, - /** PUT */ - HttpPUT, - /** DELETE */ - HttpDELETE, - /** TRACE */ - HttpTRACE, - /** OPTIONS */ - HttpOPTIONS, - /** CONNECT */ - HttpCONNECT, - /** PATCH */ - HttpPATCH, - /** Unknown HTTP method */ - HttpMethodUnknown - }; - - /** A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - HttpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that allocates a new HTTP request header with only the first line filled. Object will be created without further fields. - * The user can then add fields using addField() methods - * @param[in] method The HTTP method used in this HTTP request - * @param[in] uri The URI of the first line - * @param[in] version HTTP version to be used in this request - */ - HttpRequestLayer(HttpMethod method, const std::string& uri, HttpVersion version); - - virtual ~HttpRequestLayer(); - - /** - * A copy constructor for this layer. This copy constructor inherits base copy constructor HttpMessage#HttpMessage() and add the functionality - * of copying the first line as well - * @param[in] other The instance to copy from - */ - HttpRequestLayer(const HttpRequestLayer& other); - - /** - * An assignment operator overload for this layer. This method inherits base assignment operator HttpMessage#operator=() and add the functionality - * of copying the first line as well - * @param[in] other The instance to copy from - * @return A reference to the assignee - */ - HttpRequestLayer& operator=(const HttpRequestLayer& other); - - /** - * @return A pointer to the first line instance for this message - */ - HttpRequestFirstLine* getFirstLine() const { return m_FirstLine; } - - /** - * The URL is hostname+uri. So given the following URL, for example: "www.cnn.com/main.html", the hostname is "www.cnn.com" and the URI - * is "/.main.html". URI and hostname are split to 2 different places inside the HTTP request packet: URI is in the first line and hostname - * is in "HOST" field. - * This methods concatenates the hostname and URI to the full URL - * @return The URL of the HTTP request message - */ - std::string getUrl() const; - - // implement Layer's abstract methods - std::string toString() const; - - private: - HttpRequestFirstLine* m_FirstLine; - }; - - - - - - // -------- Class HttpResponseLayer ----------------- - - class HttpResponseFirstLine; - - - /** - * @class HttpResponseLayer - * Represents an HTTP response header and inherits all basic functionality of HttpMessage and TextBasedProtocolMessage. - * The functionality that is added for this class is the HTTP first line concept. An HTTP response has the following first line: - * 200 OK HTTP/1.1 - * Since it's not an "ordinary" HTTP field, it requires a special treatment and gets a class of it's own: HttpResponseFirstLine. - * Unlike most L2-4 protocols, an HTTP response header can spread over more than 1 packet. PcapPlusPlus currently doesn't support a header - * that is spread over more than 1 packet so in such cases: 1) only the first packet will be parsed as HttpResponseLayer (the other packets - * won't be recognized as HttpResponseLayer) and 2) the HTTP header for the first packet won't be complete (as it continues in the following - * packets), this why PcapPlusPlus can indicate that HTTP response header is complete or not (doesn't end with "\r\n\r\n" or "\n\n") using - * HttpMessage#isHeaderComplete() - */ - class HttpResponseLayer : public HttpMessage - { - friend class HttpResponseFirstLine; - public: - /** - * Enum for HTTP response status codes - */ - enum HttpResponseStatusCode - { - /** 100 Continue*/ - Http100Continue, - /** 101 Switching Protocols*/ - Http101SwitchingProtocols, - /** 102 Processing */ - Http102Processing, - /** 200 OK */ - Http200OK, - /** 201 Created */ - Http201Created, - /** 202 Accepted */ - Http202Accepted, - /** 203 Non-Authoritative Information */ - Http203NonAuthoritativeInformation, - /** 204 No Content*/ - Http204NoContent, - /** 205 Reset Content*/ - Http205ResetContent, - /** 206 Partial Content */ - Http206PartialContent, - /** 207 Multi-Status */ - Http207MultiStatus, - /** 208 Already Reported */ - Http208AlreadyReported, - /** 226 IM Used */ - Http226IMUsed, - /** 300 Multiple Choices */ - Http300MultipleChoices, - /** 301 Moved Permanently */ - Http301MovedPermanently, - /** 302 (various messages) */ - Http302, - /** 303 See Other */ - Http303SeeOther, - /** 304 Not Modified */ - Http304NotModified, - /** 305 Use Proxy */ - Http305UseProxy, - /** 306 Switch Proxy */ - Http306SwitchProxy, - /** 307 Temporary Redirect */ - Http307TemporaryRedirect, - /** 308 Permanent Redirect, */ - Http308PermanentRedirect, - /** 400 Bad Request */ - Http400BadRequest, - /** 401 Unauthorized */ - Http401Unauthorized, - /** 402 Payment Required */ - Http402PaymentRequired, - /** 403 Forbidden */ - Http403Forbidden, - /** 404 Not Found */ - Http404NotFound, - /** 405 Method Not Allowed */ - Http405MethodNotAllowed, - /** 406 Not Acceptable */ - Http406NotAcceptable, - /** 407 Proxy Authentication Required */ - Http407ProxyAuthenticationRequired, - /** 408 Request Timeout */ - Http408RequestTimeout, - /** 409 Conflict */ - Http409Conflict, - /** 410 Gone */ - Http410Gone, - /** 411 Length Required */ - Http411LengthRequired, - /** 412 Precondition Failed */ - Http412PreconditionFailed, - /** 413 RequestEntity Too Large */ - Http413RequestEntityTooLarge, - /** 414 Request-URI Too Long */ - Http414RequestURITooLong, - /** 415 Unsupported Media Type */ - Http415UnsupportedMediaType, - /** 416 Requested Range Not Satisfiable */ - Http416RequestedRangeNotSatisfiable, - /** 417 Expectation Failed */ - Http417ExpectationFailed, - /** 418 I'm a teapot */ - Http418Imateapot, - /** 419 Authentication Timeout */ - Http419AuthenticationTimeout, - /** 420 (various messages) */ - Http420, - /** 422 Unprocessable Entity */ - Http422UnprocessableEntity, - /** 423 Locked */ - Http423Locked, - /** 424 Failed Dependency */ - Http424FailedDependency, - /** 426 Upgrade Required */ - Http426UpgradeRequired, - /** 428 Precondition Required */ - Http428PreconditionRequired, - /** 429 Too Many Requests */ - Http429TooManyRequests, - /** 431 Request Header Fields Too Large */ - Http431RequestHeaderFieldsTooLarge, - /** 440 Login Timeout */ - Http440LoginTimeout, - /** 444 No Response */ - Http444NoResponse, - /** 449 Retry With */ - Http449RetryWith, - /** 450 Blocked by Windows Parental Controls */ - Http450BlockedByWindowsParentalControls, - /** 451 (various messages) */ - Http451, - /** 494 Request Header Too Large */ - Http494RequestHeaderTooLarge, - /** 495 Cert Error */ - Http495CertError, - /** 496 No Cert */ - Http496NoCert, - /** 497 HTTP to HTTPS */ - Http497HTTPtoHTTPS, - /** 498 Token expired/invalid */ - Http498TokenExpiredInvalid, - /** 499 (various messages) */ - Http499, - /** 500 Internal Server Error */ - Http500InternalServerError, - /** 501 Not Implemented */ - Http501NotImplemented, - /** 502 Bad Gateway */ - Http502BadGateway, - /** 503 Service Unavailable */ - Http503ServiceUnavailable, - /** 504 Gateway Timeout */ - Http504GatewayTimeout, - /** 505 HTTP Version Not Supported */ - Http505HTTPVersionNotSupported, - /** 506 Variant Also Negotiates */ - Http506VariantAlsoNegotiates, - /** 507 Insufficient Storage */ - Http507InsufficientStorage, - /** 508 Loop Detected */ - Http508LoopDetected, - /** 509 Bandwidth Limit Exceeded */ - Http509BandwidthLimitExceeded, - /** 510 Not Extended */ - Http510NotExtended, - /** 511 Network Authentication Required */ - Http511NetworkAuthenticationRequired, - /** 520 Origin Error */ - Http520OriginError, - /** 521 Web server is down */ - Http521WebServerIsDown, - /** 522 Connection timed out */ - Http522ConnectionTimedOut, - /** 523 Proxy Declined Request */ - Http523ProxyDeclinedRequest, - /** 524 A timeout occurred */ - Http524aTimeoutOccurred, - /** 598 Network read timeout error */ - Http598NetworkReadTimeoutError, - /** 599 Network connect timeout error */ - Http599NetworkConnectTimeoutError, - /** Unknown status code */ - HttpStatusCodeUnknown - }; - - - /** A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - HttpResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that allocates a new HTTP response header with only the first line filled. Object will be created without further fields. - * The user can then add fields using addField() methods - * @param[in] version HTTP version to be used - * @param[in] statusCode Status code to be used - * @param[in] statusCodeString Most status codes have their default string, e.g 200 is usually "OK", 404 is usually "Not Found", etc. - * But the user can set a non-default status code string and it will be written in the header first line. Empty string ("") means using the - * default status code string - */ - HttpResponseLayer(HttpVersion version, HttpResponseLayer::HttpResponseStatusCode statusCode, std::string statusCodeString = ""); - - virtual ~HttpResponseLayer(); - - /** - * A copy constructor for this layer. This copy constructor inherits base copy constructor HttpMessage#HttpMessage() and adds the functionality - * of copying the first line as well - * @param[in] other The instance to copy from - */ - HttpResponseLayer(const HttpResponseLayer& other); - - /** - * An assignment operator overload for this layer. This method inherits base assignment operator HttpMessage#operator=() and adds the functionality - * of copying the first line as well - * @param[in] other The instance to copy from - * @return A reference to the assignee - */ - HttpResponseLayer& operator=(const HttpResponseLayer& other); - - /** - * @return A pointer to the first line instance for this message - */ - HttpResponseFirstLine* getFirstLine() const { return m_FirstLine; } - - /** - * The length of the body of many HTTP response messages is determined by a HTTP header field called "Content-Length". This method sets - * The content-length field value. The method supports several cases: - * - If the "Content-Length" field exists - the method will only replace the existing value with the new value - * - If the "Content-Length" field doesn't exist - the method will create this field and put the value in it. Here are also 2 cases: - * - If prevFieldName is specified - the new "Content-Length" field will be created after it - * - If prevFieldName isn't specified or doesn't exist - the new "Content-Length" field will be created as the last field before - * end-of-header field - * @param[in] contentLength The content length value to set - * @param[in] prevFieldName Optional field, if specified and "Content-Length" field doesn't exist, it will be created after it - * @return A pointer to the "Content-Length" field, or NULL if creation failed for some reason - */ - HeaderField* setContentLength(int contentLength, const std::string &prevFieldName = ""); - - /** - * The length of the body of many HTTP response messages is determined by a HTTP header field called "Content-Length". This method - * parses this field, extracts its value and return it. If this field doesn't exist the method will return 0 - * @return HTTP response body length determined by "Content-Length" field - */ - int getContentLength() const; - - // implement Layer's abstract methods - - std::string toString() const; - - private: - HttpResponseFirstLine* m_FirstLine; - - }; - - - - - - // -------- Class HttpRequestFirstLine ----------------- - - /** - * @class HttpRequestFirstLine - * Represents an HTTP request header first line. The first line includes 3 parameters: HTTP method (e.g GET, POST, etc.), - * URI (e.g /main/index.html) and HTTP version (e.g HTTP/1.1). All these parameters are included in this class, and the user - * can retrieve or set them. - * This class cannot be instantiated by users, it's created inside HttpRequestLayer and user can get a pointer to an instance of it. All "get" - * methods of this class will retrieve the actual data of the HTTP request and the "set" methods will change the packet data. - * Since HTTP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So most "set" methods - * of this class need in most cases to shorten or extend the data in HttpRequestLayer. These methods will return a false value if this - * action failed - */ - class HttpRequestFirstLine - { - friend class HttpRequestLayer; - public: - /** - * @return The HTTP method - */ - HttpRequestLayer::HttpMethod getMethod() const { return m_Method; } - - /** - * Set the HTTP method - * @param[in] newMethod The method to set - * @return False if newMethod is HttpRequestLayer#HttpMethodUnknown or if shortening/extending the HttpRequestLayer data failed. True otherwise - */ - bool setMethod(HttpRequestLayer::HttpMethod newMethod); - - /** - * @return A copied version of the URI (notice changing the return value won't change the actual data of the packet) - */ - std::string getUri() const; - - /** - * Set the URI - * @param[in] newUri The URI to set - * @return False if shortening/extending the HttpRequestLayer data failed. True otherwise - */ - bool setUri(std::string newUri); - - /** - * @return The HTTP version - */ - HttpVersion getVersion() const { return m_Version; } - - /** - * Set the HTTP version. This method doesn't return a value since all supported HTTP versions are of the same size - * (HTTP/0.9, HTTP/1.0, HTTP/1.1) - * @param[in] newVersion The HTTP version to set - */ - void setVersion(HttpVersion newVersion); - - /** - * A static method for parsing the HTTP method out of raw data - * @param[in] data The raw data - * @param[in] dataLen The raw data length - * @return The parsed HTTP method - */ - static HttpRequestLayer::HttpMethod parseMethod(char* data, size_t dataLen); - - /** - * @return The size in bytes of the HTTP first line - */ - int getSize() const { return m_FirstLineEndOffset; } - - /** - * As explained in HttpRequestLayer, an HTTP header can spread over more than 1 packet, so when looking at a single packet - * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication - * whether the first line is partial - * @return False if the first line is partial, true if it's complete - */ - bool isComplete() const { return m_IsComplete; } - - /** - * @class HttpRequestFirstLineException - * This exception can be thrown while constructing HttpRequestFirstLine (the constructor is private, so the construction happens - * only in HttpRequestLayer). This kind of exception will be thrown if trying to construct with HTTP method of - * HttpRequestLayer#HttpMethodUnknown or with undefined HTTP version ::HttpVersionUnknown - */ - class HttpRequestFirstLineException : public std::exception - { - public: - ~HttpRequestFirstLineException() throw() {} - void setMessage(const std::string &message) { m_Message = message; } - virtual const char* what() const throw() - { - return m_Message.c_str(); - } - private: - std::string m_Message; - }; - private: - HttpRequestFirstLine(HttpRequestLayer* httpRequest); - HttpRequestFirstLine(HttpRequestLayer* httpRequest, HttpRequestLayer::HttpMethod method, HttpVersion version, const std::string& uri = "/"); - //throw(HttpRequestFirstLineException); // Deprecated in C++17 - - void parseVersion(); - - HttpRequestLayer* m_HttpRequest; - HttpRequestLayer::HttpMethod m_Method; - HttpVersion m_Version; - int m_VersionOffset; - int m_UriOffset; - int m_FirstLineEndOffset; - bool m_IsComplete; - HttpRequestFirstLineException m_Exception; - }; - - - - - - // -------- Class HttpResponseFirstLine ----------------- - - /** - * @class HttpResponseFirstLine - * Represents an HTTP response header first line. The first line includes 2 parameters: status code (e.g 200 OK, 404 Not Found, etc.), - * and HTTP version (e.g HTTP/1.1). These 2 parameters are included in this class, and the user can retrieve or set them. - * This class cannot be instantiated by users, it's created inside HttpResponseLayer and user can get a pointer to an instance of it. The "get" - * methods of this class will retrieve the actual data of the HTTP response and the "set" methods will change the packet data. - * Since HTTP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So most "set" methods - * of this class need in most cases to shorten or extend the data in HttpResponseLayer. These methods will return a false value if this - * action failed - */ - class HttpResponseFirstLine - { - friend class HttpResponseLayer; - public: - /** - * @return The status code as HttpResponseLayer::HttpResponseStatusCode enum - */ - HttpResponseLayer::HttpResponseStatusCode getStatusCode() const { return m_StatusCode; } - - /** - * @return The status code number as integer (e.g 200, 404, etc.) - */ - int getStatusCodeAsInt() const; - - /** - * @return The status code message (e.g "OK", "Not Found", etc.) - */ - std::string getStatusCodeString() const; - - /** - * Set the status code - * @param[in] newStatusCode The new status code to set - * @param[in] statusCodeString An optional parameter: set a non-default status code message (e.g "Bla Bla" instead of "Not Found"). If - * this parameter isn't supplied or supplied as empty string (""), the default message for the status code will be set - * @return True if setting the status code was completed successfully, false otherwise - */ - bool setStatusCode(HttpResponseLayer::HttpResponseStatusCode newStatusCode, std::string statusCodeString = ""); - - /** - * @return The HTTP version - */ - HttpVersion getVersion() const { return m_Version; } - - /** - * Set the HTTP version. This method doesn't return a value since all supported HTTP versions are of the same size - * (HTTP/0.9, HTTP/1.0, HTTP/1.1) - * @param[in] newVersion The HTTP version to set - */ - void setVersion(HttpVersion newVersion); - - /** - * A static method for parsing the HTTP status code out of raw data - * @param[in] data The raw data - * @param[in] dataLen The raw data length - * @return The parsed HTTP status code as enum - */ - static HttpResponseLayer::HttpResponseStatusCode parseStatusCode(char* data, size_t dataLen); - - /** - * @return The size in bytes of the HTTP first line - */ - int getSize() const { return m_FirstLineEndOffset; } - - /** - * As explained in HttpResponseLayer, an HTTP header can spread over more than 1 packet, so when looking at a single packet - * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication - * whether the first line is partial - * @return False if the first line is partial, true if it's complete - */ - bool isComplete() const { return m_IsComplete; } - - /** - * @class HttpResponseFirstLineException - * This exception can be thrown while constructing HttpResponseFirstLine (the constructor is private, so the construction happens - * only in HttpResponseLayer). This kind of exception will be thrown if trying to construct with HTTP status code of - * HttpResponseLayer#HttpStatusCodeUnknown or with undefined HTTP version ::HttpVersionUnknown - */ - class HttpResponseFirstLineException : public std::exception - { - public: - ~HttpResponseFirstLineException() throw() {} - void setMessage(const std::string &message) { m_Message = message; } - virtual const char* what() const throw() - { - return m_Message.c_str(); - } - private: - std::string m_Message; - }; - - private: - HttpResponseFirstLine(HttpResponseLayer* httpResponse); - HttpResponseFirstLine(HttpResponseLayer* httpResponse, HttpVersion version, HttpResponseLayer::HttpResponseStatusCode statusCode, std::string statusCodeString = ""); - - static HttpVersion parseVersion(char* data, size_t dataLen); - static HttpResponseLayer::HttpResponseStatusCode validateStatusCode(char* data, size_t dataLen, HttpResponseLayer::HttpResponseStatusCode potentialCode); - - - HttpResponseLayer* m_HttpResponse; - HttpVersion m_Version; - HttpResponseLayer::HttpResponseStatusCode m_StatusCode; - int m_FirstLineEndOffset; - bool m_IsComplete; - HttpResponseFirstLineException m_Exception; - }; - -} // namespace pcpp - -#endif /* PACKETPP_HTTP_LAYER */ +#ifndef PACKETPP_HTTP_LAYER +#define PACKETPP_HTTP_LAYER + +#include "TextBasedProtocol.h" +#include +#include + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + + /** + * An enum for HTTP version + */ + enum HttpVersion + { + /** HTTP/0.9 */ + ZeroDotNine, + /** HTTP/1.0 */ + OneDotZero, + /** HTTP/1.1 */ + OneDotOne, + /** Unknown HTTP version */ + HttpVersionUnknown + }; + + // some popular HTTP fields + + /** Host field */ +#define PCPP_HTTP_HOST_FIELD "Host" + /** Connection field */ +#define PCPP_HTTP_CONNECTION_FIELD "Connection" + /** User-Agent field */ +#define PCPP_HTTP_USER_AGENT_FIELD "User-Agent" + /** Referer field */ +#define PCPP_HTTP_REFERER_FIELD "Referer" + /** Accept field */ +#define PCPP_HTTP_ACCEPT_FIELD "Accept" + /** Accept-Encoding field */ +#define PCPP_HTTP_ACCEPT_ENCODING_FIELD "Accept-Encoding" + /** Accept-Language field */ +#define PCPP_HTTP_ACCEPT_LANGUAGE_FIELD "Accept-Language" + /** Cookie field */ +#define PCPP_HTTP_COOKIE_FIELD "Cookie" + /** Content-Length field */ +#define PCPP_HTTP_CONTENT_LENGTH_FIELD "Content-Length" + /** Content-Encoding field */ +#define PCPP_HTTP_CONTENT_ENCODING_FIELD "Content-Encoding" + /** Content-Type field */ +#define PCPP_HTTP_CONTENT_TYPE_FIELD "Content-Type" + /** Transfer-Encoding field */ +#define PCPP_HTTP_TRANSFER_ENCODING_FIELD "Transfer-Encoding" + /** Server field */ +#define PCPP_HTTP_SERVER_FIELD "Server" + + + + // -------- Class HttpMessage ----------------- + + + /** + * @class HttpMessage + * Represents a general HTTP message. It's an abstract class and cannot be instantiated. It's inherited by HttpRequestLayer and HttpResponseLayer + */ + class HttpMessage : public TextBasedProtocolMessage + { + public: + + virtual ~HttpMessage() {} + + /** + * A static method that checks whether the port is considered as HTTP + * @param[in] port The port number to be checked + * @return True if the port matches those associated with the HTTP protocol + */ + static bool isHttpPort(uint16_t port) { return port == 80 || port == 8080; } + + // overridden methods + + virtual HeaderField* addField(const std::string& fieldName, const std::string& fieldValue); + virtual HeaderField* addField(const HeaderField& newField); + virtual HeaderField* insertField(HeaderField* prevField, const std::string& fieldName, const std::string& fieldValue); + virtual HeaderField* insertField(HeaderField* prevField, const HeaderField& newField); + + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } + + protected: + HttpMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : TextBasedProtocolMessage(data, dataLen, prevLayer, packet) {} + HttpMessage() : TextBasedProtocolMessage() {} + HttpMessage(const HttpMessage& other) : TextBasedProtocolMessage(other) {} + HttpMessage& operator=(const HttpMessage& other) { TextBasedProtocolMessage::operator=(other); return *this; } + + // implementation of abstract methods + char getHeaderFieldNameValueSeparator() const { return ':'; } + bool spacesAllowedBetweenHeaderFieldNameAndValue() const { return true; } + }; + + + + + class HttpRequestFirstLine; + + + + // -------- Class HttpRequestLayer ----------------- + + /** + * @class HttpRequestLayer + * Represents an HTTP request header and inherits all basic functionality of HttpMessage and TextBasedProtocolMessage. + * The functionality that is added for this class is the HTTP first line concept. An HTTP request has the following first line: + * GET /bla/blabla.asp HTTP/1.1 + * Since it's not an "ordinary" HTTP field, it requires a special treatment and gets a class of it's own: HttpRequestFirstLine. + * Unlike most L2-4 protocols, an HTTP request header can spread over more than 1 packet. PcapPlusPlus currently doesn't support a header + * that is spread over more than 1 packet so in such cases: 1) only the first packet will be parsed as HttpRequestLayer (the other packets + * won't be recognized as HttpRequestLayer) and 2) the HTTP header for the first packet won't be complete (as it continues in the following + * packets), this why PcapPlusPlus can indicate that HTTP request header is complete or not(doesn't end with "\r\n\r\n" or "\n\n") using + * HttpMessage#isHeaderComplete() + */ + class HttpRequestLayer : public HttpMessage + { + friend class HttpRequestFirstLine; + public: + /** + * HTTP request methods + */ + enum HttpMethod + { + /** GET */ + HttpGET, + /** HEAD */ + HttpHEAD, + /** POST */ + HttpPOST, + /** PUT */ + HttpPUT, + /** DELETE */ + HttpDELETE, + /** TRACE */ + HttpTRACE, + /** OPTIONS */ + HttpOPTIONS, + /** CONNECT */ + HttpCONNECT, + /** PATCH */ + HttpPATCH, + /** Unknown HTTP method */ + HttpMethodUnknown + }; + + /** A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + HttpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * A constructor that allocates a new HTTP request header with only the first line filled. Object will be created without further fields. + * The user can then add fields using addField() methods + * @param[in] method The HTTP method used in this HTTP request + * @param[in] uri The URI of the first line + * @param[in] version HTTP version to be used in this request + */ + HttpRequestLayer(HttpMethod method, const std::string& uri, HttpVersion version); + + virtual ~HttpRequestLayer(); + + /** + * A copy constructor for this layer. This copy constructor inherits base copy constructor HttpMessage#HttpMessage() and add the functionality + * of copying the first line as well + * @param[in] other The instance to copy from + */ + HttpRequestLayer(const HttpRequestLayer& other); + + /** + * An assignment operator overload for this layer. This method inherits base assignment operator HttpMessage#operator=() and add the functionality + * of copying the first line as well + * @param[in] other The instance to copy from + * @return A reference to the assignee + */ + HttpRequestLayer& operator=(const HttpRequestLayer& other); + + /** + * @return A pointer to the first line instance for this message + */ + HttpRequestFirstLine* getFirstLine() const { return m_FirstLine; } + + /** + * The URL is hostname+uri. So given the following URL, for example: "www.cnn.com/main.html", the hostname is "www.cnn.com" and the URI + * is "/.main.html". URI and hostname are split to 2 different places inside the HTTP request packet: URI is in the first line and hostname + * is in "HOST" field. + * This methods concatenates the hostname and URI to the full URL + * @return The URL of the HTTP request message + */ + std::string getUrl() const; + + // implement Layer's abstract methods + std::string toString() const; + + private: + HttpRequestFirstLine* m_FirstLine; + }; + + + + + + // -------- Class HttpResponseLayer ----------------- + + class HttpResponseFirstLine; + + + /** + * @class HttpResponseLayer + * Represents an HTTP response header and inherits all basic functionality of HttpMessage and TextBasedProtocolMessage. + * The functionality that is added for this class is the HTTP first line concept. An HTTP response has the following first line: + * 200 OK HTTP/1.1 + * Since it's not an "ordinary" HTTP field, it requires a special treatment and gets a class of it's own: HttpResponseFirstLine. + * Unlike most L2-4 protocols, an HTTP response header can spread over more than 1 packet. PcapPlusPlus currently doesn't support a header + * that is spread over more than 1 packet so in such cases: 1) only the first packet will be parsed as HttpResponseLayer (the other packets + * won't be recognized as HttpResponseLayer) and 2) the HTTP header for the first packet won't be complete (as it continues in the following + * packets), this why PcapPlusPlus can indicate that HTTP response header is complete or not (doesn't end with "\r\n\r\n" or "\n\n") using + * HttpMessage#isHeaderComplete() + */ + class HttpResponseLayer : public HttpMessage + { + friend class HttpResponseFirstLine; + public: + /** + * Enum for HTTP response status codes + */ + enum HttpResponseStatusCode + { + /** 100 Continue*/ + Http100Continue, + /** 101 Switching Protocols*/ + Http101SwitchingProtocols, + /** 102 Processing */ + Http102Processing, + /** 200 OK */ + Http200OK, + /** 201 Created */ + Http201Created, + /** 202 Accepted */ + Http202Accepted, + /** 203 Non-Authoritative Information */ + Http203NonAuthoritativeInformation, + /** 204 No Content*/ + Http204NoContent, + /** 205 Reset Content*/ + Http205ResetContent, + /** 206 Partial Content */ + Http206PartialContent, + /** 207 Multi-Status */ + Http207MultiStatus, + /** 208 Already Reported */ + Http208AlreadyReported, + /** 226 IM Used */ + Http226IMUsed, + /** 300 Multiple Choices */ + Http300MultipleChoices, + /** 301 Moved Permanently */ + Http301MovedPermanently, + /** 302 (various messages) */ + Http302, + /** 303 See Other */ + Http303SeeOther, + /** 304 Not Modified */ + Http304NotModified, + /** 305 Use Proxy */ + Http305UseProxy, + /** 306 Switch Proxy */ + Http306SwitchProxy, + /** 307 Temporary Redirect */ + Http307TemporaryRedirect, + /** 308 Permanent Redirect, */ + Http308PermanentRedirect, + /** 400 Bad Request */ + Http400BadRequest, + /** 401 Unauthorized */ + Http401Unauthorized, + /** 402 Payment Required */ + Http402PaymentRequired, + /** 403 Forbidden */ + Http403Forbidden, + /** 404 Not Found */ + Http404NotFound, + /** 405 Method Not Allowed */ + Http405MethodNotAllowed, + /** 406 Not Acceptable */ + Http406NotAcceptable, + /** 407 Proxy Authentication Required */ + Http407ProxyAuthenticationRequired, + /** 408 Request Timeout */ + Http408RequestTimeout, + /** 409 Conflict */ + Http409Conflict, + /** 410 Gone */ + Http410Gone, + /** 411 Length Required */ + Http411LengthRequired, + /** 412 Precondition Failed */ + Http412PreconditionFailed, + /** 413 RequestEntity Too Large */ + Http413RequestEntityTooLarge, + /** 414 Request-URI Too Long */ + Http414RequestURITooLong, + /** 415 Unsupported Media Type */ + Http415UnsupportedMediaType, + /** 416 Requested Range Not Satisfiable */ + Http416RequestedRangeNotSatisfiable, + /** 417 Expectation Failed */ + Http417ExpectationFailed, + /** 418 I'm a teapot */ + Http418Imateapot, + /** 419 Authentication Timeout */ + Http419AuthenticationTimeout, + /** 420 (various messages) */ + Http420, + /** 422 Unprocessable Entity */ + Http422UnprocessableEntity, + /** 423 Locked */ + Http423Locked, + /** 424 Failed Dependency */ + Http424FailedDependency, + /** 426 Upgrade Required */ + Http426UpgradeRequired, + /** 428 Precondition Required */ + Http428PreconditionRequired, + /** 429 Too Many Requests */ + Http429TooManyRequests, + /** 431 Request Header Fields Too Large */ + Http431RequestHeaderFieldsTooLarge, + /** 440 Login Timeout */ + Http440LoginTimeout, + /** 444 No Response */ + Http444NoResponse, + /** 449 Retry With */ + Http449RetryWith, + /** 450 Blocked by Windows Parental Controls */ + Http450BlockedByWindowsParentalControls, + /** 451 (various messages) */ + Http451, + /** 494 Request Header Too Large */ + Http494RequestHeaderTooLarge, + /** 495 Cert Error */ + Http495CertError, + /** 496 No Cert */ + Http496NoCert, + /** 497 HTTP to HTTPS */ + Http497HTTPtoHTTPS, + /** 498 Token expired/invalid */ + Http498TokenExpiredInvalid, + /** 499 (various messages) */ + Http499, + /** 500 Internal Server Error */ + Http500InternalServerError, + /** 501 Not Implemented */ + Http501NotImplemented, + /** 502 Bad Gateway */ + Http502BadGateway, + /** 503 Service Unavailable */ + Http503ServiceUnavailable, + /** 504 Gateway Timeout */ + Http504GatewayTimeout, + /** 505 HTTP Version Not Supported */ + Http505HTTPVersionNotSupported, + /** 506 Variant Also Negotiates */ + Http506VariantAlsoNegotiates, + /** 507 Insufficient Storage */ + Http507InsufficientStorage, + /** 508 Loop Detected */ + Http508LoopDetected, + /** 509 Bandwidth Limit Exceeded */ + Http509BandwidthLimitExceeded, + /** 510 Not Extended */ + Http510NotExtended, + /** 511 Network Authentication Required */ + Http511NetworkAuthenticationRequired, + /** 520 Origin Error */ + Http520OriginError, + /** 521 Web server is down */ + Http521WebServerIsDown, + /** 522 Connection timed out */ + Http522ConnectionTimedOut, + /** 523 Proxy Declined Request */ + Http523ProxyDeclinedRequest, + /** 524 A timeout occurred */ + Http524aTimeoutOccurred, + /** 598 Network read timeout error */ + Http598NetworkReadTimeoutError, + /** 599 Network connect timeout error */ + Http599NetworkConnectTimeoutError, + /** Unknown status code */ + HttpStatusCodeUnknown + }; + + + /** A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + HttpResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * A constructor that allocates a new HTTP response header with only the first line filled. Object will be created without further fields. + * The user can then add fields using addField() methods + * @param[in] version HTTP version to be used + * @param[in] statusCode Status code to be used + * @param[in] statusCodeString Most status codes have their default string, e.g 200 is usually "OK", 404 is usually "Not Found", etc. + * But the user can set a non-default status code string and it will be written in the header first line. Empty string ("") means using the + * default status code string + */ + HttpResponseLayer(HttpVersion version, HttpResponseLayer::HttpResponseStatusCode statusCode, std::string statusCodeString = ""); + + virtual ~HttpResponseLayer(); + + /** + * A copy constructor for this layer. This copy constructor inherits base copy constructor HttpMessage#HttpMessage() and adds the functionality + * of copying the first line as well + * @param[in] other The instance to copy from + */ + HttpResponseLayer(const HttpResponseLayer& other); + + /** + * An assignment operator overload for this layer. This method inherits base assignment operator HttpMessage#operator=() and adds the functionality + * of copying the first line as well + * @param[in] other The instance to copy from + * @return A reference to the assignee + */ + HttpResponseLayer& operator=(const HttpResponseLayer& other); + + /** + * @return A pointer to the first line instance for this message + */ + HttpResponseFirstLine* getFirstLine() const { return m_FirstLine; } + + /** + * The length of the body of many HTTP response messages is determined by a HTTP header field called "Content-Length". This method sets + * The content-length field value. The method supports several cases: + * - If the "Content-Length" field exists - the method will only replace the existing value with the new value + * - If the "Content-Length" field doesn't exist - the method will create this field and put the value in it. Here are also 2 cases: + * - If prevFieldName is specified - the new "Content-Length" field will be created after it + * - If prevFieldName isn't specified or doesn't exist - the new "Content-Length" field will be created as the last field before + * end-of-header field + * @param[in] contentLength The content length value to set + * @param[in] prevFieldName Optional field, if specified and "Content-Length" field doesn't exist, it will be created after it + * @return A pointer to the "Content-Length" field, or NULL if creation failed for some reason + */ + HeaderField* setContentLength(int contentLength, const std::string &prevFieldName = ""); + + /** + * The length of the body of many HTTP response messages is determined by a HTTP header field called "Content-Length". This method + * parses this field, extracts its value and return it. If this field doesn't exist the method will return 0 + * @return HTTP response body length determined by "Content-Length" field + */ + int getContentLength() const; + + // implement Layer's abstract methods + + std::string toString() const; + + private: + HttpResponseFirstLine* m_FirstLine; + + }; + + + + + + // -------- Class HttpRequestFirstLine ----------------- + + /** + * @class HttpRequestFirstLine + * Represents an HTTP request header first line. The first line includes 3 parameters: HTTP method (e.g GET, POST, etc.), + * URI (e.g /main/index.html) and HTTP version (e.g HTTP/1.1). All these parameters are included in this class, and the user + * can retrieve or set them. + * This class cannot be instantiated by users, it's created inside HttpRequestLayer and user can get a pointer to an instance of it. All "get" + * methods of this class will retrieve the actual data of the HTTP request and the "set" methods will change the packet data. + * Since HTTP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So most "set" methods + * of this class need in most cases to shorten or extend the data in HttpRequestLayer. These methods will return a false value if this + * action failed + */ + class HttpRequestFirstLine + { + friend class HttpRequestLayer; + public: + /** + * @return The HTTP method + */ + HttpRequestLayer::HttpMethod getMethod() const { return m_Method; } + + /** + * Set the HTTP method + * @param[in] newMethod The method to set + * @return False if newMethod is HttpRequestLayer#HttpMethodUnknown or if shortening/extending the HttpRequestLayer data failed. True otherwise + */ + bool setMethod(HttpRequestLayer::HttpMethod newMethod); + + /** + * @return A copied version of the URI (notice changing the return value won't change the actual data of the packet) + */ + std::string getUri() const; + + /** + * Set the URI + * @param[in] newUri The URI to set + * @return False if shortening/extending the HttpRequestLayer data failed. True otherwise + */ + bool setUri(std::string newUri); + + /** + * @return The HTTP version + */ + HttpVersion getVersion() const { return m_Version; } + + /** + * Set the HTTP version. This method doesn't return a value since all supported HTTP versions are of the same size + * (HTTP/0.9, HTTP/1.0, HTTP/1.1) + * @param[in] newVersion The HTTP version to set + */ + void setVersion(HttpVersion newVersion); + + /** + * A static method for parsing the HTTP method out of raw data + * @param[in] data The raw data + * @param[in] dataLen The raw data length + * @return The parsed HTTP method + */ + static HttpRequestLayer::HttpMethod parseMethod(char* data, size_t dataLen); + + /** + * @return The size in bytes of the HTTP first line + */ + int getSize() const { return m_FirstLineEndOffset; } + + /** + * As explained in HttpRequestLayer, an HTTP header can spread over more than 1 packet, so when looking at a single packet + * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication + * whether the first line is partial + * @return False if the first line is partial, true if it's complete + */ + bool isComplete() const { return m_IsComplete; } + + /** + * @class HttpRequestFirstLineException + * This exception can be thrown while constructing HttpRequestFirstLine (the constructor is private, so the construction happens + * only in HttpRequestLayer). This kind of exception will be thrown if trying to construct with HTTP method of + * HttpRequestLayer#HttpMethodUnknown or with undefined HTTP version ::HttpVersionUnknown + */ + class HttpRequestFirstLineException : public std::exception + { + public: + ~HttpRequestFirstLineException() throw() {} + void setMessage(const std::string &message) { m_Message = message; } + virtual const char* what() const throw() + { + return m_Message.c_str(); + } + private: + std::string m_Message; + }; + private: + HttpRequestFirstLine(HttpRequestLayer* httpRequest); + HttpRequestFirstLine(HttpRequestLayer* httpRequest, HttpRequestLayer::HttpMethod method, HttpVersion version, const std::string& uri = "/"); + //throw(HttpRequestFirstLineException); // Deprecated in C++17 + + void parseVersion(); + + HttpRequestLayer* m_HttpRequest; + HttpRequestLayer::HttpMethod m_Method; + HttpVersion m_Version; + int m_VersionOffset; + int m_UriOffset; + int m_FirstLineEndOffset; + bool m_IsComplete; + HttpRequestFirstLineException m_Exception; + }; + + + + + + // -------- Class HttpResponseFirstLine ----------------- + + /** + * @class HttpResponseFirstLine + * Represents an HTTP response header first line. The first line includes 2 parameters: status code (e.g 200 OK, 404 Not Found, etc.), + * and HTTP version (e.g HTTP/1.1). These 2 parameters are included in this class, and the user can retrieve or set them. + * This class cannot be instantiated by users, it's created inside HttpResponseLayer and user can get a pointer to an instance of it. The "get" + * methods of this class will retrieve the actual data of the HTTP response and the "set" methods will change the packet data. + * Since HTTP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So most "set" methods + * of this class need in most cases to shorten or extend the data in HttpResponseLayer. These methods will return a false value if this + * action failed + */ + class HttpResponseFirstLine + { + friend class HttpResponseLayer; + public: + /** + * @return The status code as HttpResponseLayer::HttpResponseStatusCode enum + */ + HttpResponseLayer::HttpResponseStatusCode getStatusCode() const { return m_StatusCode; } + + /** + * @return The status code number as integer (e.g 200, 404, etc.) + */ + int getStatusCodeAsInt() const; + + /** + * @return The status code message (e.g "OK", "Not Found", etc.) + */ + std::string getStatusCodeString() const; + + /** + * Set the status code + * @param[in] newStatusCode The new status code to set + * @param[in] statusCodeString An optional parameter: set a non-default status code message (e.g "Bla Bla" instead of "Not Found"). If + * this parameter isn't supplied or supplied as empty string (""), the default message for the status code will be set + * @return True if setting the status code was completed successfully, false otherwise + */ + bool setStatusCode(HttpResponseLayer::HttpResponseStatusCode newStatusCode, std::string statusCodeString = ""); + + /** + * @return The HTTP version + */ + HttpVersion getVersion() const { return m_Version; } + + /** + * Set the HTTP version. This method doesn't return a value since all supported HTTP versions are of the same size + * (HTTP/0.9, HTTP/1.0, HTTP/1.1) + * @param[in] newVersion The HTTP version to set + */ + void setVersion(HttpVersion newVersion); + + /** + * A static method for parsing the HTTP status code out of raw data + * @param[in] data The raw data + * @param[in] dataLen The raw data length + * @return The parsed HTTP status code as enum + */ + static HttpResponseLayer::HttpResponseStatusCode parseStatusCode(char* data, size_t dataLen); + + /** + * @return The size in bytes of the HTTP first line + */ + int getSize() const { return m_FirstLineEndOffset; } + + /** + * As explained in HttpResponseLayer, an HTTP header can spread over more than 1 packet, so when looking at a single packet + * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication + * whether the first line is partial + * @return False if the first line is partial, true if it's complete + */ + bool isComplete() const { return m_IsComplete; } + + /** + * @class HttpResponseFirstLineException + * This exception can be thrown while constructing HttpResponseFirstLine (the constructor is private, so the construction happens + * only in HttpResponseLayer). This kind of exception will be thrown if trying to construct with HTTP status code of + * HttpResponseLayer#HttpStatusCodeUnknown or with undefined HTTP version ::HttpVersionUnknown + */ + class HttpResponseFirstLineException : public std::exception + { + public: + ~HttpResponseFirstLineException() throw() {} + void setMessage(const std::string &message) { m_Message = message; } + virtual const char* what() const throw() + { + return m_Message.c_str(); + } + private: + std::string m_Message; + }; + + private: + HttpResponseFirstLine(HttpResponseLayer* httpResponse); + HttpResponseFirstLine(HttpResponseLayer* httpResponse, HttpVersion version, HttpResponseLayer::HttpResponseStatusCode statusCode, std::string statusCodeString = ""); + + static HttpVersion parseVersion(char* data, size_t dataLen); + static HttpResponseLayer::HttpResponseStatusCode validateStatusCode(char* data, size_t dataLen, HttpResponseLayer::HttpResponseStatusCode potentialCode); + + + HttpResponseLayer* m_HttpResponse; + HttpVersion m_Version; + HttpResponseLayer::HttpResponseStatusCode m_StatusCode; + int m_FirstLineEndOffset; + bool m_IsComplete; + HttpResponseFirstLineException m_Exception; + }; + +} // namespace pcpp + +#endif /* PACKETPP_HTTP_LAYER */ diff --git a/Packet++/header/IcmpV6Layer.h b/Packet++/header/IcmpV6Layer.h index 23322153c7..f686fdc01c 100644 --- a/Packet++/header/IcmpV6Layer.h +++ b/Packet++/header/IcmpV6Layer.h @@ -1,282 +1,282 @@ -#ifndef PACKETPP_ICMPV6_LAYER -#define PACKETPP_ICMPV6_LAYER - -#include "Layer.h" - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - -/** - * An enum representing the available ICMPv6 message types - */ -enum class ICMPv6MessageType : int -{ - /** Unknown ICMPv6 message */ - ICMPv6_UNKNOWN_MESSAGE = 0, - /** Destination Unreachable Message */ - ICMPv6_DESTINATION_UNREACHABLE = 1, - /** Packet Too Big Message */ - ICMPv6_PACKET_TOO_BIG = 2, - /** Time Exceeded Message */ - ICMPv6_TIME_EXCEEDED = 3, - /** Parameter Problem Message */ - ICMPv6_PARAMETER_PROBLEM = 4, - /** Private Experimentation Message */ - ICMPv6_PRIVATE_EXPERIMENTATION1 = 100, - /** Private Experimentation Message */ - ICMPv6_PRIVATE_EXPERIMENTATION2 = 101, - /** Reserved for expansion of ICMPv6 error messages */ - ICMPv6_RESERVED_EXPANSION_ERROR = 127, - /** Echo Request Message */ - ICMPv6_ECHO_REQUEST = 128, - /** Echo Reply Message */ - ICMPv6_ECHO_REPLY = 129, - /** Multicast Listener Query Message */ - ICMPv6_MULTICAST_LISTENER_QUERY = 130, - /** Multicast Listener Report Message */ - ICMPv6_MULTICAST_LISTENER_REPORT = 131, - /** Multicast Listener Done Message */ - ICMPv6_MULTICAST_LISTENER_DONE = 132, - /** Router Solicitation Message */ - ICMPv6_ROUTER_SOLICITATION = 133, - /** Router Advertisement Message */ - ICMPv6_ROUTER_ADVERTISEMENT = 134, - /** Neighbor Solicitation Message */ - ICMPv6_NEIGHBOR_SOLICITATION = 135, - /** Neighbor Advertisement Message */ - ICMPv6_NEIGHBOR_ADVERTISEMENT = 136, - /** Redirect Message */ - ICMPv6_REDIRECT_MESSAGE = 137, - /** Router Renumbering Message */ - ICMPv6_ROUTER_RENUMBERING = 138, - /** Node Information Query Message */ - ICMPv6_ICMP_NODE_INFORMATION_QUERY = 139, - /** Node Information Reply Message*/ - ICMPv6_ICMP_NODE_INFORMATION_RESPONSE = 140, - /** Inverse Neighbor Discovery Solicitation Message */ - ICMPv6_INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION_MESSAGE = 141, - /** Inverse Neighbor Discovery Advertisement Message */ - ICMPv6_INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT_MESSAGE = 142, - /** Multicast Listener Report Message */ - ICMPv6_MULTICAST_LISTENER_DISCOVERY_REPORTS = 143, - /** Home Agent Address Discovery Request Message */ - ICMPv6_HOME_AGENT_ADDRESS_DISCOVERY_REQUEST_MESSAGE = 144, - /** Home Agent Address Discovery Reply Message */ - ICMPv6_HOME_AGENT_ADDRESS_DISCOVERY_REPLY_MESSAGE = 145, - /** Mobile Prefix Solicitation Message */ - ICMPv6_MOBILE_PREFIX_SOLICITATION = 146, - /** Mobile Prefix Advertisement Message */ - ICMPv6_MOBILE_PREFIX_ADVERTISEMENT = 147, - /** Certification Path Solicitation Message */ - ICMPv6_CERTIFICATION_PATH_SOLICITATION = 148, - /** Certification Path Advertisement Message */ - ICMPv6_CERTIFICATION_PATH_ADVERTISEMENT = 149, - /** ICMP Experimental Mobility Subtype Format and Registry Message */ - ICMPv6_EXPERIMENTAL_MOBILITY = 150, - /** Multicast Router Advertisement Message */ - ICMPv6_MULTICAST_ROUTER_ADVERTISEMENT = 151, - /** Multicast Router Solicitation Message */ - ICMPv6_MULTICAST_ROUTER_SOLICITATION = 152, - /** Multicast Router Termination Message*/ - ICMPv6_MULTICAST_ROUTER_TERMINATION = 153, - /** RPL Control Message */ - ICMPv6_RPL_CONTROL_MESSAGE = 155, - /** Private Experimentation Message */ - ICMPv6_PRIVATE_EXPERIMENTATION3 = 200, - /** Private Experimentation Message */ - ICMPv6_PRIVATE_EXPERIMENTATION4 = 201, - /** Reserved for expansion of ICMPv6 informational messages */ - ICMPv6_RESERVED_EXPANSION_INFORMATIONAL = 255 -}; - -/** - * @struct icmpv6hdr - * Represents an ICMPv6 protocol header - */ -#pragma pack(push, 1) -struct icmpv6hdr -{ - /** Type of the message. Values in the range from 0 to 127 (high-order bit is 0) indicate an error message, - while values in the range from 128 to 255 (high-order bit is 1) indicate an information message. */ - uint8_t type; - /** The code field value depends on the message type and provides an additional level of message granularity */ - uint8_t code; - /** The checksum field provides a minimal level of integrity verification for the ICMP message */ - uint16_t checksum; -}; -#pragma pack(pop) - -/** - * @struct icmpv6_echo_hdr - * ICMP echo request/reply message structure - */ -#pragma pack(push, 1) -typedef struct icmpv6_echo_hdr : icmpv6hdr -{ - /** the echo request identifier */ - uint16_t id; - /** the echo request sequence number */ - uint16_t sequence; -} icmpv6_echo_hdr; -#pragma pack(pop) - -/** - * @class IcmpV6Layer - * Base class for ICMPv6 protocol layers which provides common logic for ICMPv6 messages. - */ -class IcmpV6Layer : public Layer -{ -public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param data A pointer to the raw data - * @param dataLen Size of the data in bytes - * @param prevLayer A pointer to the previous layer - * @param packet A pointer to the Packet instance where layer will be stored in - */ - IcmpV6Layer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : Layer(data, dataLen, prevLayer, packet) { m_Protocol = ICMPv6; } - - /** - * A constructor that allocates a new ICMPv6 layer with type, code and data - * @param[in] msgType Message type of the ICMPv6 layer - * @param[in] code Code field of the ICMPv6 layer - * @param[in] data A pointer to the payload to set - * @param[in] dataLen The length of the payload - */ - IcmpV6Layer(ICMPv6MessageType msgType, uint8_t code, const uint8_t *data, size_t dataLen); - - virtual ~IcmpV6Layer() {} - - /** - * A static method that creates an ICMPv6 layer from packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored - * @return Layer* A newly allocated ICMPv6 layer - */ - static Layer *parseIcmpV6Layer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet); - - /** - * @param[in] type Type to check - * @return True if the layer if of the given type, false otherwise - */ - bool isMessageOfType(ICMPv6MessageType type) const { return getMessageType() == type; } - - /** - * @return Get the ICMPv6 Message Type - */ - ICMPv6MessageType getMessageType() const; - - /** - * @return Get the code header field - */ - uint8_t getCode() const; - - /** - * @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() {} - - /** - * @return The size of the ICMPv6 message - */ - size_t getHeaderLen() const { return m_DataLen; } - - /** - * Calculate ICMPv6 checksum field - */ - void computeCalculateFields(); - - OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } - - std::string toString() const; - -protected: - IcmpV6Layer() = default; - -private: - void calculateChecksum(); - icmpv6hdr *getIcmpv6Header() const { return (icmpv6hdr *)m_Data; } -}; - -/** - * @class ICMPv6EchoLayer - * Represents an ICMPv6 echo request/reply protocol layer - */ -class ICMPv6EchoLayer : public IcmpV6Layer -{ -public: - /** - * An enum representing ICMPv6 echo message types - */ - enum ICMPv6EchoType - { - /** Echo Request Type */ - REQUEST, - /** Echo Reply Type */ - REPLY - }; - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - ICMPv6EchoLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : IcmpV6Layer(data, dataLen, prevLayer, packet) {} - - /** - * A constructor for a new echo request/reply layer - * @param[in] echoType The type of the echo message - * @param[in] id Echo request identifier - * @param[in] sequence Echo request sequence number - * @param[in] data A pointer to echo request payload to set - * @param[in] dataLen The length of the echo request payload - */ - ICMPv6EchoLayer(ICMPv6EchoType echoType, uint16_t id, uint16_t sequence, const uint8_t *data, size_t dataLen); - - virtual ~ICMPv6EchoLayer() {} - - /** - * @return Identifier in host representation - */ - uint16_t getIdentifier() const; - - /** - * @return Sequence number in host representation - */ - uint16_t getSequenceNr() const; - - /** - * @return Size of the data in bytes - */ - size_t getEchoDataLen() const { return m_DataLen - sizeof(icmpv6_echo_hdr); } - - /** - * @return Pointer to the beginning of the data - */ - uint8_t *getEchoDataPtr() const { return m_Data + sizeof(icmpv6_echo_hdr); } - - std::string toString() const; - -private: - icmpv6_echo_hdr *getEchoHeader() const { return (icmpv6_echo_hdr *)m_Data; } -}; - -} // namespace pcpp -#endif /* PACKETPP_ICMPV6_LAYER */ +#ifndef PACKETPP_ICMPV6_LAYER +#define PACKETPP_ICMPV6_LAYER + +#include "Layer.h" + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + +/** + * An enum representing the available ICMPv6 message types + */ +enum class ICMPv6MessageType : int +{ + /** Unknown ICMPv6 message */ + ICMPv6_UNKNOWN_MESSAGE = 0, + /** Destination Unreachable Message */ + ICMPv6_DESTINATION_UNREACHABLE = 1, + /** Packet Too Big Message */ + ICMPv6_PACKET_TOO_BIG = 2, + /** Time Exceeded Message */ + ICMPv6_TIME_EXCEEDED = 3, + /** Parameter Problem Message */ + ICMPv6_PARAMETER_PROBLEM = 4, + /** Private Experimentation Message */ + ICMPv6_PRIVATE_EXPERIMENTATION1 = 100, + /** Private Experimentation Message */ + ICMPv6_PRIVATE_EXPERIMENTATION2 = 101, + /** Reserved for expansion of ICMPv6 error messages */ + ICMPv6_RESERVED_EXPANSION_ERROR = 127, + /** Echo Request Message */ + ICMPv6_ECHO_REQUEST = 128, + /** Echo Reply Message */ + ICMPv6_ECHO_REPLY = 129, + /** Multicast Listener Query Message */ + ICMPv6_MULTICAST_LISTENER_QUERY = 130, + /** Multicast Listener Report Message */ + ICMPv6_MULTICAST_LISTENER_REPORT = 131, + /** Multicast Listener Done Message */ + ICMPv6_MULTICAST_LISTENER_DONE = 132, + /** Router Solicitation Message */ + ICMPv6_ROUTER_SOLICITATION = 133, + /** Router Advertisement Message */ + ICMPv6_ROUTER_ADVERTISEMENT = 134, + /** Neighbor Solicitation Message */ + ICMPv6_NEIGHBOR_SOLICITATION = 135, + /** Neighbor Advertisement Message */ + ICMPv6_NEIGHBOR_ADVERTISEMENT = 136, + /** Redirect Message */ + ICMPv6_REDIRECT_MESSAGE = 137, + /** Router Renumbering Message */ + ICMPv6_ROUTER_RENUMBERING = 138, + /** Node Information Query Message */ + ICMPv6_ICMP_NODE_INFORMATION_QUERY = 139, + /** Node Information Reply Message*/ + ICMPv6_ICMP_NODE_INFORMATION_RESPONSE = 140, + /** Inverse Neighbor Discovery Solicitation Message */ + ICMPv6_INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION_MESSAGE = 141, + /** Inverse Neighbor Discovery Advertisement Message */ + ICMPv6_INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT_MESSAGE = 142, + /** Multicast Listener Report Message */ + ICMPv6_MULTICAST_LISTENER_DISCOVERY_REPORTS = 143, + /** Home Agent Address Discovery Request Message */ + ICMPv6_HOME_AGENT_ADDRESS_DISCOVERY_REQUEST_MESSAGE = 144, + /** Home Agent Address Discovery Reply Message */ + ICMPv6_HOME_AGENT_ADDRESS_DISCOVERY_REPLY_MESSAGE = 145, + /** Mobile Prefix Solicitation Message */ + ICMPv6_MOBILE_PREFIX_SOLICITATION = 146, + /** Mobile Prefix Advertisement Message */ + ICMPv6_MOBILE_PREFIX_ADVERTISEMENT = 147, + /** Certification Path Solicitation Message */ + ICMPv6_CERTIFICATION_PATH_SOLICITATION = 148, + /** Certification Path Advertisement Message */ + ICMPv6_CERTIFICATION_PATH_ADVERTISEMENT = 149, + /** ICMP Experimental Mobility Subtype Format and Registry Message */ + ICMPv6_EXPERIMENTAL_MOBILITY = 150, + /** Multicast Router Advertisement Message */ + ICMPv6_MULTICAST_ROUTER_ADVERTISEMENT = 151, + /** Multicast Router Solicitation Message */ + ICMPv6_MULTICAST_ROUTER_SOLICITATION = 152, + /** Multicast Router Termination Message*/ + ICMPv6_MULTICAST_ROUTER_TERMINATION = 153, + /** RPL Control Message */ + ICMPv6_RPL_CONTROL_MESSAGE = 155, + /** Private Experimentation Message */ + ICMPv6_PRIVATE_EXPERIMENTATION3 = 200, + /** Private Experimentation Message */ + ICMPv6_PRIVATE_EXPERIMENTATION4 = 201, + /** Reserved for expansion of ICMPv6 informational messages */ + ICMPv6_RESERVED_EXPANSION_INFORMATIONAL = 255 +}; + +/** + * @struct icmpv6hdr + * Represents an ICMPv6 protocol header + */ +#pragma pack(push, 1) +struct icmpv6hdr +{ + /** Type of the message. Values in the range from 0 to 127 (high-order bit is 0) indicate an error message, + while values in the range from 128 to 255 (high-order bit is 1) indicate an information message. */ + uint8_t type; + /** The code field value depends on the message type and provides an additional level of message granularity */ + uint8_t code; + /** The checksum field provides a minimal level of integrity verification for the ICMP message */ + uint16_t checksum; +}; +#pragma pack(pop) + +/** + * @struct icmpv6_echo_hdr + * ICMP echo request/reply message structure + */ +#pragma pack(push, 1) +typedef struct icmpv6_echo_hdr : icmpv6hdr +{ + /** the echo request identifier */ + uint16_t id; + /** the echo request sequence number */ + uint16_t sequence; +} icmpv6_echo_hdr; +#pragma pack(pop) + +/** + * @class IcmpV6Layer + * Base class for ICMPv6 protocol layers which provides common logic for ICMPv6 messages. + */ +class IcmpV6Layer : public Layer +{ +public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param data A pointer to the raw data + * @param dataLen Size of the data in bytes + * @param prevLayer A pointer to the previous layer + * @param packet A pointer to the Packet instance where layer will be stored in + */ + IcmpV6Layer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) + : Layer(data, dataLen, prevLayer, packet) { m_Protocol = ICMPv6; } + + /** + * A constructor that allocates a new ICMPv6 layer with type, code and data + * @param[in] msgType Message type of the ICMPv6 layer + * @param[in] code Code field of the ICMPv6 layer + * @param[in] data A pointer to the payload to set + * @param[in] dataLen The length of the payload + */ + IcmpV6Layer(ICMPv6MessageType msgType, uint8_t code, const uint8_t *data, size_t dataLen); + + virtual ~IcmpV6Layer() {} + + /** + * A static method that creates an ICMPv6 layer from packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored + * @return Layer* A newly allocated ICMPv6 layer + */ + static Layer *parseIcmpV6Layer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet); + + /** + * @param[in] type Type to check + * @return True if the layer if of the given type, false otherwise + */ + bool isMessageOfType(ICMPv6MessageType type) const { return getMessageType() == type; } + + /** + * @return Get the ICMPv6 Message Type + */ + ICMPv6MessageType getMessageType() const; + + /** + * @return Get the code header field + */ + uint8_t getCode() const; + + /** + * @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() {} + + /** + * @return The size of the ICMPv6 message + */ + size_t getHeaderLen() const { return m_DataLen; } + + /** + * Calculate ICMPv6 checksum field + */ + void computeCalculateFields(); + + OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } + + std::string toString() const; + +protected: + IcmpV6Layer() = default; + +private: + void calculateChecksum(); + icmpv6hdr *getIcmpv6Header() const { return (icmpv6hdr *)m_Data; } +}; + +/** + * @class ICMPv6EchoLayer + * Represents an ICMPv6 echo request/reply protocol layer + */ +class ICMPv6EchoLayer : public IcmpV6Layer +{ +public: + /** + * An enum representing ICMPv6 echo message types + */ + enum ICMPv6EchoType + { + /** Echo Request Type */ + REQUEST, + /** Echo Reply Type */ + REPLY + }; + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + ICMPv6EchoLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) + : IcmpV6Layer(data, dataLen, prevLayer, packet) {} + + /** + * A constructor for a new echo request/reply layer + * @param[in] echoType The type of the echo message + * @param[in] id Echo request identifier + * @param[in] sequence Echo request sequence number + * @param[in] data A pointer to echo request payload to set + * @param[in] dataLen The length of the echo request payload + */ + ICMPv6EchoLayer(ICMPv6EchoType echoType, uint16_t id, uint16_t sequence, const uint8_t *data, size_t dataLen); + + virtual ~ICMPv6EchoLayer() {} + + /** + * @return Identifier in host representation + */ + uint16_t getIdentifier() const; + + /** + * @return Sequence number in host representation + */ + uint16_t getSequenceNr() const; + + /** + * @return Size of the data in bytes + */ + size_t getEchoDataLen() const { return m_DataLen - sizeof(icmpv6_echo_hdr); } + + /** + * @return Pointer to the beginning of the data + */ + uint8_t *getEchoDataPtr() const { return m_Data + sizeof(icmpv6_echo_hdr); } + + std::string toString() const; + +private: + icmpv6_echo_hdr *getEchoHeader() const { return (icmpv6_echo_hdr *)m_Data; } +}; + +} // namespace pcpp +#endif /* PACKETPP_ICMPV6_LAYER */ diff --git a/Packet++/header/IgmpLayer.h b/Packet++/header/IgmpLayer.h index 12608e33e9..e5041e69e0 100644 --- a/Packet++/header/IgmpLayer.h +++ b/Packet++/header/IgmpLayer.h @@ -1,514 +1,514 @@ -#ifndef PACKETPP_IGMP_LAYER -#define PACKETPP_IGMP_LAYER - -#include "Layer.h" -#include "IpAddress.h" -#include - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - -/** - * @struct igmp_header - * IGMPv1 and IGMPv2 basic protocol header - */ -struct igmp_header -{ - /** Indicates the message type. The enum for message type is pcpp::IgmpType */ - uint8_t type; - /** Specifies the time limit for the corresponding report. The field has a resolution of 100 milliseconds */ - uint8_t maxResponseTime; - /** This is the 16-bit one's complement of the one's complement sum of the entire IGMP message */ - uint16_t checksum; - /** This is the multicast address being queried when sending a Group-Specific or Group-and-Source-Specific Query */ - uint32_t groupAddress; -}; - - -/** - * @struct igmpv3_query_header - * IGMPv3 membership query basic header - */ -struct igmpv3_query_header -{ - /** IGMP message type. Should always have value of membership query (::IgmpType_MembershipQuery) */ - uint8_t type; - /** This field specifies the maximum time (in 1/10 second) allowed before sending a responding report */ - uint8_t maxResponseTime; - /** This is the 16-bit one's complement of the one's complement sum of the entire IGMP message */ - uint16_t checksum; - /** This is the multicast address being queried when sending a Group-Specific or Group-and-Source-Specific Query */ - uint32_t groupAddress; - /** Suppress Router-side Processing Flag + Querier's Robustness Variable */ - uint8_t s_qrv; - /** Querier's Query Interval Code */ - uint8_t qqic; - /** This field specifies the number of source addresses present in the Query */ - uint16_t numOfSources; -}; - - -/** - * @struct igmpv3_report_header - * IGMPv3 membership report basic header - */ -struct igmpv3_report_header -{ - /** IGMP message type. Should always have value of IGMPv3 membership report (::IgmpType_MembershipReportV3) */ - uint8_t type; - /** Unused byte */ - uint8_t reserved1; - /** This is the 16-bit one's complement of the one's complement sum of the entire IGMP message */ - uint16_t checksum; - /** Unused bytes */ - uint16_t reserved2; - /** This field specifies the number of group records present in the Report */ - uint16_t numOfGroupRecords; -}; - - -/** - * @struct igmpv3_group_record - * A block of fields containing information pertaining to the sender's membership in a single multicast group on the interface - * from which the Report is sent. Relevant only for IGMPv3 membership report messages - */ -struct igmpv3_group_record -{ - /** Group record type */ - uint8_t recordType; - /** Contains the length of the Auxiliary Data field in this Group Record. A value other than 0 isn't supported */ - uint8_t auxDataLen; - /** Specifies how many source addresses are present in this Group Record */ - uint16_t numOfSources; - /** Contains the IP multicast address to which this Group Record pertains */ - uint32_t multicastAddress; - /** A vector of n IP unicast addresses, where n is the value in this record's Number of Sources field */ - uint8_t sourceAddresses[]; - - /** - * @return The multicast address in igmpv3_group_record#multicastAddress as IPv4Address instance - */ - IPv4Address getMulticastAddress() const { return multicastAddress; } - - /** - * @return The number of source addresses in this group record - */ - uint16_t getSourceAddressCount() const; - - /** - * Get the source address at a certain index - * @param[in] index The index of the source address in the group record - * @return The source address in the requested index. If index is negative or higher than the number of source addresses in this - * group record the value if IPv4Address#Zero is returned - */ - IPv4Address getSourceAddressAtIndex(int index) const; - - /** - * @return The total size in bytes of the group record - */ - size_t getRecordLen() const; -}; - - -/** - * IGMP message types - */ -enum IgmpType -{ - /** Unknown message type */ - IgmpType_Unknown = 0, - /** IGMP Membership Query */ - IgmpType_MembershipQuery = 0x11, - /** IGMPv1 Membership Report */ - IgmpType_MembershipReportV1 = 0x12, - /** DVMRP */ - IgmpType_DVMRP = 0x13, - /** PIM version 1 */ - IgmpType_P1Mv1 = 0x14, - /** Cisco Trace Messages */ - IgmpType_CiscoTrace = 0x15, - /** IGMPv2 Membership Report */ - IgmpType_MembershipReportV2 = 0x16, - /** IGMPv2 Leave Group */ - IgmpType_LeaveGroup = 0x17, - /** Multicast Traceroute Response */ - IgmpType_MulticastTracerouteResponse = 0x1e, - /** Multicast Traceroute */ - IgmpType_MulticastTraceroute = 0x1f, - /** IGMPv3 Membership Report */ - IgmpType_MembershipReportV3 = 0x22, - /** MRD, Multicast Router Advertisement */ - IgmpType_MulticastRouterAdvertisement = 0x30, - /** MRD, Multicast Router Solicitation */ - IgmpType_MulticastRouterSolicitation = 0x31, - /** MRD, Multicast Router Termination */ - IgmpType_MulticastRouterTermination = 0x32, -}; - - -/** - * @class IgmpLayer - * A base class for all IGMP (Internet Group Management Protocol) protocol classes. This is an abstract class and cannot be instantiated, - * only its child classes can be instantiated. The inherited classes represent the different versions of the protocol: - * IGMPv1, IGMPv2 and IGMPv3 - */ -class IgmpLayer : public Layer -{ -protected: - - IgmpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, ProtocolType igmpVer) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = igmpVer; } - - IgmpLayer(IgmpType type, const IPv4Address& groupAddr, uint8_t maxResponseTime, ProtocolType igmpVer); - - uint16_t calculateChecksum(); - - size_t getHeaderSizeByVerAndType(ProtocolType igmpVer, IgmpType igmpType) const; -public: - - virtual ~IgmpLayer() {} - - /** - * Get a pointer to the raw IGMPv1/IGMPv2 header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref igmp_header - */ - igmp_header* getIgmpHeader() const { return (igmp_header*)m_Data; } - - /** - * @return The IPv4 multicast address stored igmp_header#groupAddress - */ - IPv4Address getGroupAddress() const { return getIgmpHeader()->groupAddress; } - - /** - * Set the IPv4 multicast address - * @param[in] groupAddr The IPv4 address to set - */ - void setGroupAddress(const IPv4Address& groupAddr); - - /** - * @return IGMP type set in igmp_header#type as ::IgmpType enum. Notice that if igmp_header#type contains a value - * that doesn't appear in the ::IgmpType enum, ::IgmpType_Unknown will be returned - */ - IgmpType getType() const; - - /** - * Set IGMP type (will be written to igmp_header#type field) - * @param[in] type The type to set - */ - void setType(IgmpType type); - - /** - * A static method that gets raw IGMP data (byte stream) and returns the IGMP version of this IGMP message - * @param[in] data The IGMP raw data (byte stream) - * @param[in] dataLen Raw data length - * @param[out] isQuery Return true if IGMP message type is ::IgmpType_MembershipQuery and false otherwise - * @return One of the values ::IGMPv1, ::IGMPv2, ::IGMPv3 according to detected IGMP version or ::UnknownProtocol if couldn't detect - * IGMP version - */ - static ProtocolType getIGMPVerFromData(uint8_t* data, size_t dataLen, bool& isQuery); - - - // implement abstract methods - - /** - * Does nothing for this layer (IGMP layer is always last) - */ - void parseNextLayer() {} - - /** - * @return Size of IGMP header = 8B - */ - size_t getHeaderLen() const { return sizeof(igmp_header); } - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } -}; - - -/** - * @class IgmpV1Layer - * Represents IGMPv1 (Internet Group Management Protocol ver 1) layer. This class represents all the different messages of IGMPv1 - */ -class IgmpV1Layer : public IgmpLayer -{ -public: - /** A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - IgmpV1Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv1) {} - - /** - * A constructor that allocates a new IGMPv1 header - * @param[in] type The message type to set - * @param[in] groupAddr The multicast address to set. This is an optional parameter and has a default value of IPv4Address#Zero - * if not provided - */ - explicit IgmpV1Layer(IgmpType type, const IPv4Address& groupAddr = IPv4Address()) - : IgmpLayer(type, groupAddr, 0, IGMPv1) {} - - /** - * A destructor for this layer (does nothing) - */ - ~IgmpV1Layer() {} - - - // implement abstract methods - - /** - * Calculate the IGMP checksum and set igmp_header#maxResponseTime to 0 (this field is unused in IGMPv1) - */ - void computeCalculateFields(); - -}; - - -/** - * @class IgmpV2Layer - * Represents IGMPv2 (Internet Group Management Protocol ver 2) layer. This class represents all the different messages of IGMPv2 - */ -class IgmpV2Layer : public IgmpLayer -{ -public: - /** A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - IgmpV2Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv2) {} - - /** - * A constructor that allocates a new IGMPv2 header - * @param[in] type The message type to set - * @param[in] groupAddr The multicast address to set. This is an optional parameter and has a default value of unspecified/zero IPv4 address - * @param[in] maxResponseTime The max response time to set. This is an optional parameter and has a default value of 0 if not provided - */ - explicit IgmpV2Layer(IgmpType type, const IPv4Address& groupAddr = IPv4Address(), uint8_t maxResponseTime = 0) - : IgmpLayer(type, groupAddr, maxResponseTime, IGMPv2) {} - - /** - * A destructor for this layer (does nothing) - */ - ~IgmpV2Layer() {} - - - // implement abstract methods - - /** - * Calculate the IGMP checksum - */ - void computeCalculateFields(); -}; - - -/** - * @class IgmpV3QueryLayer - * Represents an IGMPv3 (Internet Group Management Protocol ver 3) membership query message - */ -class IgmpV3QueryLayer : public IgmpLayer -{ -public: - - /** A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - IgmpV3QueryLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that allocates a new IGMPv3 membership query - * @param[in] multicastAddr The multicast address to set. This is an optional parameter and has a default value of unspecified/zero IPv4 address - * if not provided - * @param[in] maxResponseTime The max response time to set. This is an optional parameter and has a default value of 0 if not provided - * @param[in] s_qrv A 1-byte value representing the value in Suppress Router-side Processing Flag + Querier's Robustness Variable - * (igmpv3_query_header#s_qrv field). This is an optional parameter and has a default value of 0 if not provided - */ - explicit IgmpV3QueryLayer(const IPv4Address& multicastAddr = IPv4Address(), uint8_t maxResponseTime = 0, uint8_t s_qrv = 0); - - /** - * Get a pointer to the raw IGMPv3 membership query header. Notice this points directly to the data, so every change will change the - * actual packet data - * @return A pointer to the @ref igmpv3_query_header - */ - igmpv3_query_header* getIgmpV3QueryHeader() const { return (igmpv3_query_header*)m_Data; } - - /** - * @return The number of source addresses in this message (as extracted from the igmpv3_query_header#numOfSources field) - */ - uint16_t getSourceAddressCount() const; - - /** - * Get the IPV4 source address in a certain index - * @param[in] index The requested index of the source address - * @return The IPv4 source address, or IPv4Address#Zero if index is out of bounds (of the message or of the layer) - */ - IPv4Address getSourceAddressAtIndex(int index) const; - - /** - * Add a new source address at the end of the source address list. The igmpv3_query_header#numOfSources field will be incremented accordingly - * @param[in] addr The IPv4 source address to add - * @return True if source address was added successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool addSourceAddress(const IPv4Address& addr); - - /** - * Add a new source address at a certain index of the source address list. The igmpv3_query_header#numOfSources field will be incremented accordingly - * @param[in] addr The IPv4 source address to add - * @param[in] index The index to add the new source address at - * @return True if source address was added successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool addSourceAddressAtIndex(const IPv4Address& addr, int index); - - /** - * Remove a source address at a certain index. The igmpv3_query_header#numOfSources field will be decremented accordingly - * @param[in] index The index of the source address to be removed - * @return True if source address was removed successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool removeSourceAddressAtIndex(int index); - - /** - * Remove all source addresses in the message. The igmpv3_query_header#numOfSources field will be set to 0 - * @return True if all source addresses were cleared successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool removeAllSourceAddresses(); - - // implement abstract methods - - /** - * Calculate the IGMP checksum - */ - void computeCalculateFields(); - - /** - * @return The message size in bytes which include the size of the basic header + the size of the source address list - */ - size_t getHeaderLen() const; -}; - - -/** - * @class IgmpV3ReportLayer - * Represents an IGMPv3 (Internet Group Management Protocol ver 3) membership report message - */ -class IgmpV3ReportLayer : public IgmpLayer -{ -private: - igmpv3_group_record* addGroupRecordAt(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses, int offset); - -public: - - /** A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - IgmpV3ReportLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv3) {} - - /** - * A constructor that allocates a new IGMPv3 membership report with 0 group addresses - */ - IgmpV3ReportLayer() : IgmpLayer(IgmpType_MembershipReportV3, IPv4Address(), 0, IGMPv3) {} - - /** - * Get a pointer to the raw IGMPv3 membership report header. Notice this points directly to the data, so every change will change the - * actual packet data - * @return A pointer to the @ref igmpv3_report_header - */ - igmpv3_report_header* getReportHeader() const { return (igmpv3_report_header*)m_Data; } - - /** - * @return The number of group records in this message (as extracted from the igmpv3_report_header#numOfGroupRecords field) - */ - uint16_t getGroupRecordCount() const; - - /** - * @return A pointer to the first group record or NULL if no group records exist. Notice the return value is a pointer to the real data, - * so changes in the return value will affect the packet data - */ - igmpv3_group_record* getFirstGroupRecord() const; - - /** - * Get the group record that comes next to a given group record. If "groupRecord" is NULL then NULL will be returned. - * If "groupRecord" is the last group record or if it is out of layer bounds NULL will be returned also. Notice the return value is a - * pointer to the real data casted to igmpv3_group_record type (as opposed to a copy of the option data). So changes in the return - * value will affect the packet data - * @param[in] groupRecord The group record to start searching from - * @return The next group record or NULL if "groupRecord" is NULL, last or out of layer bounds - */ - igmpv3_group_record* getNextGroupRecord(igmpv3_group_record* groupRecord) const; - - /** - * Add a new group record at a the end of the group record list. The igmpv3_report_header#numOfGroupRecords field will be - * incremented accordingly - * @param[in] recordType The type of the new group record - * @param[in] multicastAddress The multicast address of the new group record - * @param[in] sourceAddresses A vector containing all the source addresses of the new group record - * @return The method constructs a new group record, adds it to the end of the group record list of IGMPv3 report message and - * returns a pointer to the new message. If something went wrong in creating or adding the new group record a NULL value is returned - * and an appropriate error message is printed to log - */ - igmpv3_group_record* addGroupRecord(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses); - - /** - * Add a new group record at a certain index of the group record list. The igmpv3_report_header#numOfGroupRecords field will be - * incremented accordingly - * @param[in] recordType The type of the new group record - * @param[in] multicastAddress The multicast address of the new group record - * @param[in] sourceAddresses A vector containing all the source addresses of the new group record - * @param[in] index The index to add the new group address at - * @return The method constructs a new group record, adds it to the IGMPv3 report message and returns a pointer to the new message. - * If something went wrong in creating or adding the new group record a NULL value is returned and an appropriate error message is - * printed to log - */ - igmpv3_group_record* addGroupRecordAtIndex(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses, int index); - - /** - * Remove a group record at a certain index. The igmpv3_report_header#numOfGroupRecords field will be decremented accordingly - * @param[in] index The index of the group record to be removed - * @return True if group record was removed successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool removeGroupRecordAtIndex(int index); - - /** - * Remove all group records in the message. The igmpv3_report_header#numOfGroupRecords field will be set to 0 - * @return True if all group records were cleared successfully or false otherwise. If false is returned an appropriate error message - * will be printed to log - */ - bool removeAllGroupRecords(); - - // implement abstract methods - - /** - * Calculate the IGMP checksum - */ - void computeCalculateFields(); - - /** - * @return The message size in bytes which include the size of the basic header + the size of the group record list - */ - size_t getHeaderLen() const { return m_DataLen; } -}; - -} - -#endif // PACKETPP_IGMP_LAYER +#ifndef PACKETPP_IGMP_LAYER +#define PACKETPP_IGMP_LAYER + +#include "Layer.h" +#include "IpAddress.h" +#include + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + +/** + * @struct igmp_header + * IGMPv1 and IGMPv2 basic protocol header + */ +struct igmp_header +{ + /** Indicates the message type. The enum for message type is pcpp::IgmpType */ + uint8_t type; + /** Specifies the time limit for the corresponding report. The field has a resolution of 100 milliseconds */ + uint8_t maxResponseTime; + /** This is the 16-bit one's complement of the one's complement sum of the entire IGMP message */ + uint16_t checksum; + /** This is the multicast address being queried when sending a Group-Specific or Group-and-Source-Specific Query */ + uint32_t groupAddress; +}; + + +/** + * @struct igmpv3_query_header + * IGMPv3 membership query basic header + */ +struct igmpv3_query_header +{ + /** IGMP message type. Should always have value of membership query (::IgmpType_MembershipQuery) */ + uint8_t type; + /** This field specifies the maximum time (in 1/10 second) allowed before sending a responding report */ + uint8_t maxResponseTime; + /** This is the 16-bit one's complement of the one's complement sum of the entire IGMP message */ + uint16_t checksum; + /** This is the multicast address being queried when sending a Group-Specific or Group-and-Source-Specific Query */ + uint32_t groupAddress; + /** Suppress Router-side Processing Flag + Querier's Robustness Variable */ + uint8_t s_qrv; + /** Querier's Query Interval Code */ + uint8_t qqic; + /** This field specifies the number of source addresses present in the Query */ + uint16_t numOfSources; +}; + + +/** + * @struct igmpv3_report_header + * IGMPv3 membership report basic header + */ +struct igmpv3_report_header +{ + /** IGMP message type. Should always have value of IGMPv3 membership report (::IgmpType_MembershipReportV3) */ + uint8_t type; + /** Unused byte */ + uint8_t reserved1; + /** This is the 16-bit one's complement of the one's complement sum of the entire IGMP message */ + uint16_t checksum; + /** Unused bytes */ + uint16_t reserved2; + /** This field specifies the number of group records present in the Report */ + uint16_t numOfGroupRecords; +}; + + +/** + * @struct igmpv3_group_record + * A block of fields containing information pertaining to the sender's membership in a single multicast group on the interface + * from which the Report is sent. Relevant only for IGMPv3 membership report messages + */ +struct igmpv3_group_record +{ + /** Group record type */ + uint8_t recordType; + /** Contains the length of the Auxiliary Data field in this Group Record. A value other than 0 isn't supported */ + uint8_t auxDataLen; + /** Specifies how many source addresses are present in this Group Record */ + uint16_t numOfSources; + /** Contains the IP multicast address to which this Group Record pertains */ + uint32_t multicastAddress; + /** A vector of n IP unicast addresses, where n is the value in this record's Number of Sources field */ + uint8_t sourceAddresses[]; + + /** + * @return The multicast address in igmpv3_group_record#multicastAddress as IPv4Address instance + */ + IPv4Address getMulticastAddress() const { return multicastAddress; } + + /** + * @return The number of source addresses in this group record + */ + uint16_t getSourceAddressCount() const; + + /** + * Get the source address at a certain index + * @param[in] index The index of the source address in the group record + * @return The source address in the requested index. If index is negative or higher than the number of source addresses in this + * group record the value if IPv4Address#Zero is returned + */ + IPv4Address getSourceAddressAtIndex(int index) const; + + /** + * @return The total size in bytes of the group record + */ + size_t getRecordLen() const; +}; + + +/** + * IGMP message types + */ +enum IgmpType +{ + /** Unknown message type */ + IgmpType_Unknown = 0, + /** IGMP Membership Query */ + IgmpType_MembershipQuery = 0x11, + /** IGMPv1 Membership Report */ + IgmpType_MembershipReportV1 = 0x12, + /** DVMRP */ + IgmpType_DVMRP = 0x13, + /** PIM version 1 */ + IgmpType_P1Mv1 = 0x14, + /** Cisco Trace Messages */ + IgmpType_CiscoTrace = 0x15, + /** IGMPv2 Membership Report */ + IgmpType_MembershipReportV2 = 0x16, + /** IGMPv2 Leave Group */ + IgmpType_LeaveGroup = 0x17, + /** Multicast Traceroute Response */ + IgmpType_MulticastTracerouteResponse = 0x1e, + /** Multicast Traceroute */ + IgmpType_MulticastTraceroute = 0x1f, + /** IGMPv3 Membership Report */ + IgmpType_MembershipReportV3 = 0x22, + /** MRD, Multicast Router Advertisement */ + IgmpType_MulticastRouterAdvertisement = 0x30, + /** MRD, Multicast Router Solicitation */ + IgmpType_MulticastRouterSolicitation = 0x31, + /** MRD, Multicast Router Termination */ + IgmpType_MulticastRouterTermination = 0x32, +}; + + +/** + * @class IgmpLayer + * A base class for all IGMP (Internet Group Management Protocol) protocol classes. This is an abstract class and cannot be instantiated, + * only its child classes can be instantiated. The inherited classes represent the different versions of the protocol: + * IGMPv1, IGMPv2 and IGMPv3 + */ +class IgmpLayer : public Layer +{ +protected: + + IgmpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, ProtocolType igmpVer) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = igmpVer; } + + IgmpLayer(IgmpType type, const IPv4Address& groupAddr, uint8_t maxResponseTime, ProtocolType igmpVer); + + uint16_t calculateChecksum(); + + size_t getHeaderSizeByVerAndType(ProtocolType igmpVer, IgmpType igmpType) const; +public: + + virtual ~IgmpLayer() {} + + /** + * Get a pointer to the raw IGMPv1/IGMPv2 header. Notice this points directly to the data, so every change will change the actual packet data + * @return A pointer to the @ref igmp_header + */ + igmp_header* getIgmpHeader() const { return (igmp_header*)m_Data; } + + /** + * @return The IPv4 multicast address stored igmp_header#groupAddress + */ + IPv4Address getGroupAddress() const { return getIgmpHeader()->groupAddress; } + + /** + * Set the IPv4 multicast address + * @param[in] groupAddr The IPv4 address to set + */ + void setGroupAddress(const IPv4Address& groupAddr); + + /** + * @return IGMP type set in igmp_header#type as ::IgmpType enum. Notice that if igmp_header#type contains a value + * that doesn't appear in the ::IgmpType enum, ::IgmpType_Unknown will be returned + */ + IgmpType getType() const; + + /** + * Set IGMP type (will be written to igmp_header#type field) + * @param[in] type The type to set + */ + void setType(IgmpType type); + + /** + * A static method that gets raw IGMP data (byte stream) and returns the IGMP version of this IGMP message + * @param[in] data The IGMP raw data (byte stream) + * @param[in] dataLen Raw data length + * @param[out] isQuery Return true if IGMP message type is ::IgmpType_MembershipQuery and false otherwise + * @return One of the values ::IGMPv1, ::IGMPv2, ::IGMPv3 according to detected IGMP version or ::UnknownProtocol if couldn't detect + * IGMP version + */ + static ProtocolType getIGMPVerFromData(uint8_t* data, size_t dataLen, bool& isQuery); + + + // implement abstract methods + + /** + * Does nothing for this layer (IGMP layer is always last) + */ + void parseNextLayer() {} + + /** + * @return Size of IGMP header = 8B + */ + size_t getHeaderLen() const { return sizeof(igmp_header); } + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelNetworkLayer; } +}; + + +/** + * @class IgmpV1Layer + * Represents IGMPv1 (Internet Group Management Protocol ver 1) layer. This class represents all the different messages of IGMPv1 + */ +class IgmpV1Layer : public IgmpLayer +{ +public: + /** A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + IgmpV1Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv1) {} + + /** + * A constructor that allocates a new IGMPv1 header + * @param[in] type The message type to set + * @param[in] groupAddr The multicast address to set. This is an optional parameter and has a default value of IPv4Address#Zero + * if not provided + */ + explicit IgmpV1Layer(IgmpType type, const IPv4Address& groupAddr = IPv4Address()) + : IgmpLayer(type, groupAddr, 0, IGMPv1) {} + + /** + * A destructor for this layer (does nothing) + */ + ~IgmpV1Layer() {} + + + // implement abstract methods + + /** + * Calculate the IGMP checksum and set igmp_header#maxResponseTime to 0 (this field is unused in IGMPv1) + */ + void computeCalculateFields(); + +}; + + +/** + * @class IgmpV2Layer + * Represents IGMPv2 (Internet Group Management Protocol ver 2) layer. This class represents all the different messages of IGMPv2 + */ +class IgmpV2Layer : public IgmpLayer +{ +public: + /** A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + IgmpV2Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv2) {} + + /** + * A constructor that allocates a new IGMPv2 header + * @param[in] type The message type to set + * @param[in] groupAddr The multicast address to set. This is an optional parameter and has a default value of unspecified/zero IPv4 address + * @param[in] maxResponseTime The max response time to set. This is an optional parameter and has a default value of 0 if not provided + */ + explicit IgmpV2Layer(IgmpType type, const IPv4Address& groupAddr = IPv4Address(), uint8_t maxResponseTime = 0) + : IgmpLayer(type, groupAddr, maxResponseTime, IGMPv2) {} + + /** + * A destructor for this layer (does nothing) + */ + ~IgmpV2Layer() {} + + + // implement abstract methods + + /** + * Calculate the IGMP checksum + */ + void computeCalculateFields(); +}; + + +/** + * @class IgmpV3QueryLayer + * Represents an IGMPv3 (Internet Group Management Protocol ver 3) membership query message + */ +class IgmpV3QueryLayer : public IgmpLayer +{ +public: + + /** A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + IgmpV3QueryLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * A constructor that allocates a new IGMPv3 membership query + * @param[in] multicastAddr The multicast address to set. This is an optional parameter and has a default value of unspecified/zero IPv4 address + * if not provided + * @param[in] maxResponseTime The max response time to set. This is an optional parameter and has a default value of 0 if not provided + * @param[in] s_qrv A 1-byte value representing the value in Suppress Router-side Processing Flag + Querier's Robustness Variable + * (igmpv3_query_header#s_qrv field). This is an optional parameter and has a default value of 0 if not provided + */ + explicit IgmpV3QueryLayer(const IPv4Address& multicastAddr = IPv4Address(), uint8_t maxResponseTime = 0, uint8_t s_qrv = 0); + + /** + * Get a pointer to the raw IGMPv3 membership query header. Notice this points directly to the data, so every change will change the + * actual packet data + * @return A pointer to the @ref igmpv3_query_header + */ + igmpv3_query_header* getIgmpV3QueryHeader() const { return (igmpv3_query_header*)m_Data; } + + /** + * @return The number of source addresses in this message (as extracted from the igmpv3_query_header#numOfSources field) + */ + uint16_t getSourceAddressCount() const; + + /** + * Get the IPV4 source address in a certain index + * @param[in] index The requested index of the source address + * @return The IPv4 source address, or IPv4Address#Zero if index is out of bounds (of the message or of the layer) + */ + IPv4Address getSourceAddressAtIndex(int index) const; + + /** + * Add a new source address at the end of the source address list. The igmpv3_query_header#numOfSources field will be incremented accordingly + * @param[in] addr The IPv4 source address to add + * @return True if source address was added successfully or false otherwise. If false is returned an appropriate error message + * will be printed to log + */ + bool addSourceAddress(const IPv4Address& addr); + + /** + * Add a new source address at a certain index of the source address list. The igmpv3_query_header#numOfSources field will be incremented accordingly + * @param[in] addr The IPv4 source address to add + * @param[in] index The index to add the new source address at + * @return True if source address was added successfully or false otherwise. If false is returned an appropriate error message + * will be printed to log + */ + bool addSourceAddressAtIndex(const IPv4Address& addr, int index); + + /** + * Remove a source address at a certain index. The igmpv3_query_header#numOfSources field will be decremented accordingly + * @param[in] index The index of the source address to be removed + * @return True if source address was removed successfully or false otherwise. If false is returned an appropriate error message + * will be printed to log + */ + bool removeSourceAddressAtIndex(int index); + + /** + * Remove all source addresses in the message. The igmpv3_query_header#numOfSources field will be set to 0 + * @return True if all source addresses were cleared successfully or false otherwise. If false is returned an appropriate error message + * will be printed to log + */ + bool removeAllSourceAddresses(); + + // implement abstract methods + + /** + * Calculate the IGMP checksum + */ + void computeCalculateFields(); + + /** + * @return The message size in bytes which include the size of the basic header + the size of the source address list + */ + size_t getHeaderLen() const; +}; + + +/** + * @class IgmpV3ReportLayer + * Represents an IGMPv3 (Internet Group Management Protocol ver 3) membership report message + */ +class IgmpV3ReportLayer : public IgmpLayer +{ +private: + igmpv3_group_record* addGroupRecordAt(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses, int offset); + +public: + + /** A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + IgmpV3ReportLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : IgmpLayer(data, dataLen, prevLayer, packet, IGMPv3) {} + + /** + * A constructor that allocates a new IGMPv3 membership report with 0 group addresses + */ + IgmpV3ReportLayer() : IgmpLayer(IgmpType_MembershipReportV3, IPv4Address(), 0, IGMPv3) {} + + /** + * Get a pointer to the raw IGMPv3 membership report header. Notice this points directly to the data, so every change will change the + * actual packet data + * @return A pointer to the @ref igmpv3_report_header + */ + igmpv3_report_header* getReportHeader() const { return (igmpv3_report_header*)m_Data; } + + /** + * @return The number of group records in this message (as extracted from the igmpv3_report_header#numOfGroupRecords field) + */ + uint16_t getGroupRecordCount() const; + + /** + * @return A pointer to the first group record or NULL if no group records exist. Notice the return value is a pointer to the real data, + * so changes in the return value will affect the packet data + */ + igmpv3_group_record* getFirstGroupRecord() const; + + /** + * Get the group record that comes next to a given group record. If "groupRecord" is NULL then NULL will be returned. + * If "groupRecord" is the last group record or if it is out of layer bounds NULL will be returned also. Notice the return value is a + * pointer to the real data casted to igmpv3_group_record type (as opposed to a copy of the option data). So changes in the return + * value will affect the packet data + * @param[in] groupRecord The group record to start searching from + * @return The next group record or NULL if "groupRecord" is NULL, last or out of layer bounds + */ + igmpv3_group_record* getNextGroupRecord(igmpv3_group_record* groupRecord) const; + + /** + * Add a new group record at a the end of the group record list. The igmpv3_report_header#numOfGroupRecords field will be + * incremented accordingly + * @param[in] recordType The type of the new group record + * @param[in] multicastAddress The multicast address of the new group record + * @param[in] sourceAddresses A vector containing all the source addresses of the new group record + * @return The method constructs a new group record, adds it to the end of the group record list of IGMPv3 report message and + * returns a pointer to the new message. If something went wrong in creating or adding the new group record a NULL value is returned + * and an appropriate error message is printed to log + */ + igmpv3_group_record* addGroupRecord(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses); + + /** + * Add a new group record at a certain index of the group record list. The igmpv3_report_header#numOfGroupRecords field will be + * incremented accordingly + * @param[in] recordType The type of the new group record + * @param[in] multicastAddress The multicast address of the new group record + * @param[in] sourceAddresses A vector containing all the source addresses of the new group record + * @param[in] index The index to add the new group address at + * @return The method constructs a new group record, adds it to the IGMPv3 report message and returns a pointer to the new message. + * If something went wrong in creating or adding the new group record a NULL value is returned and an appropriate error message is + * printed to log + */ + igmpv3_group_record* addGroupRecordAtIndex(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses, int index); + + /** + * Remove a group record at a certain index. The igmpv3_report_header#numOfGroupRecords field will be decremented accordingly + * @param[in] index The index of the group record to be removed + * @return True if group record was removed successfully or false otherwise. If false is returned an appropriate error message + * will be printed to log + */ + bool removeGroupRecordAtIndex(int index); + + /** + * Remove all group records in the message. The igmpv3_report_header#numOfGroupRecords field will be set to 0 + * @return True if all group records were cleared successfully or false otherwise. If false is returned an appropriate error message + * will be printed to log + */ + bool removeAllGroupRecords(); + + // implement abstract methods + + /** + * Calculate the IGMP checksum + */ + void computeCalculateFields(); + + /** + * @return The message size in bytes which include the size of the basic header + the size of the group record list + */ + size_t getHeaderLen() const { return m_DataLen; } +}; + +} + +#endif // PACKETPP_IGMP_LAYER diff --git a/Packet++/header/NdpLayer.h b/Packet++/header/NdpLayer.h index 81a6dda478..43295afc44 100644 --- a/Packet++/header/NdpLayer.h +++ b/Packet++/header/NdpLayer.h @@ -1,371 +1,371 @@ -#ifndef PACKETPP_NDP_LAYER -#define PACKETPP_NDP_LAYER - -#include "IcmpV6Layer.h" -#include "IpAddress.h" -#include "Layer.h" -#include "MacAddress.h" -#include "TLVData.h" - -#include - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - -/** - * An enum representing the available option types for Neighbor Discovery in IPv6 (see RFC 4861) - */ -enum class NDPNeighborOptionTypes : int -{ - NDP_OPTION_SOURCE_LINK_LAYER = 1, - NDP_OPTION_TARGET_LINK_LAYER = 2, - NDP_OPTION_PREFIX_INFORMATION = 3, - NDP_OPTION_REDIRECTED_HEADER = 4, - NDP_OPTION_MTU = 5, - NDP_OPTION_UNKNOWN = 255 -}; - -/** - * @class NdpOption - * A wrapper class for NDP options. This class does not create or modify NDP option records, but rather - * serves as a wrapper and provides useful methods for retrieving data from them - */ -class NdpOption : public TLVRecord -{ -public: - /** - * A c'tor for this class that gets a pointer to the option raw data (byte array) - * @param[in] optionRawData A pointer to the NDP option raw data - */ - explicit NdpOption(uint8_t *optionRawData) : TLVRecord(optionRawData) {} - - /** - * A d'tor for this class, currently does nothing - */ - ~NdpOption() {} - - /** - * @return NDP option type casted as pcpp::NDPNeighborOptionTypes enum. If the data is null a value - * of NDP_OPTION_UNKNOWN is returned - */ - NDPNeighborOptionTypes getNdpOptionType() const - { - if (m_Data == NULL) - return NDPNeighborOptionTypes::NDP_OPTION_UNKNOWN; - - return static_cast(m_Data->recordType); - } - - // implement abstract methods - - size_t getTotalSize() const - { - if (m_Data == NULL) - return (size_t)0; - - return (size_t)m_Data->recordLen * 8; - } - - size_t getDataSize() const - { - if (m_Data == NULL) - return 0; - - return (size_t)m_Data->recordLen * 8 - (2 * sizeof(uint8_t)); // length value is stored in units of 8 octets - } -}; - -/** - * @class NdpOptionBuilder - * A class for building NDP option records. This builder receives the NDP option parameters in its c'tor, - * builds the NDP option raw buffer and provides a build() method to get a NdpOption object out of it - */ -class NdpOptionBuilder : public TLVRecordBuilder -{ -public: - /** - * A c'tor for building NDP options which their value is a byte array. The NdpOption object can be later - * retrieved by calling build(). Each option is padded to have a 64-bit boundary. - * @param[in] optionType NDP option type - * @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in any - * way. - * @param[in] optionValueLen Option value length in bytes - */ - NdpOptionBuilder(NDPNeighborOptionTypes optionType, const uint8_t *optionValue, uint8_t optionValueLen) - : TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) {} - - /** - * Build the NdpOption object out of the parameters defined in the c'tor. Padding bytes are added to the - * option for option length with 64-bit boundaries. - * @return The NdpOption object - */ - NdpOption build() const; -}; - -/** - * @class NDPLayerBase - * Represents a base for NDP packet types - */ -class NDPLayerBase : public IcmpV6Layer -{ -public: - virtual ~NDPLayerBase() {} - - /** - * @return The number of NDP options in this layer - */ - size_t getNdpOptionCount() const; - - /** - * Get a NDP option by type. - * @param[in] option NDP option type - * @return An NdpOption object that contains the first option that matches this type, or logical NULL - * (NdpOption#isNull() == true) if no such option found - */ - NdpOption getNdpOption(NDPNeighborOptionTypes option) const; - - /** - * @return The first NDP option in the packet. If the current layer contains no options the returned value will - * contain a logical NULL (NdpOption#isNull() == true) - */ - NdpOption getFirstNdpOption() const; - - /** - * Get the NDP option that comes after a given option. If the given option was the last one, the - * returned value will contain a logical NULL (IdpOption#isNull() == true) - * @param[in] option An NDP option object that exists in the current layer - * @return A NdpOption object that contains the NDP option data that comes next, or logical NULL if the given - * NDP option: (1) was the last one; or (2) contains a logical NULL; or (3) doesn't belong to this packet - */ - NdpOption getNextNdpOption(NdpOption &option) const; - - /** - * Add a new NDP option at the end of the layer (after the last NDP option) - * @param[in] optionBuilder An NdpOptionBuilder object that contains the NDP option data to be added - * @return A NdpOption object that contains the newly added NDP option data or logical NULL - * (NdpOption#isNull() == true) if addition failed. In case of a failure a corresponding error message will be - * printed to log - */ - NdpOption addNdpOption(const NdpOptionBuilder &optionBuilder); - - /** - * Remove all NDP options from the layer - * @return True if options removed successfully or false if some error occurred (an appropriate error message will - * be printed to log) - */ - bool removeAllNdpOptions(); - -protected: - NDPLayerBase() = default; - - NDPLayerBase(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : IcmpV6Layer(data, dataLen, prevLayer, packet) {} - -private: - TLVRecordReader m_OptionReader; - - virtual size_t getNdpHeaderLen() const = 0; - virtual uint8_t *getNdpOptionsBasePtr() const { return m_Data + getNdpHeaderLen(); }; - NdpOption addNdpOptionAt(const NdpOptionBuilder &optionBuilder, int offset); -}; - -/** - * @class NDPNeighborSolicitationLayer - * Represents a NDP Neighbor Solicitation protocol layer - */ -class NDPNeighborSolicitationLayer : public NDPLayerBase -{ -public: - /** - * @struct ndpneighborsolicitationhdr - * Represents neighbor solicitation message format - */ -#pragma pack(push, 1) - struct ndpneighborsolicitationhdr : icmpv6hdr - { - /** Reserved */ - uint32_t reserved; - /** Target address - Target address of solicitation message */ - uint8_t targetIP[16]; - }; -#pragma pack(pop) - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - NDPNeighborSolicitationLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : NDPLayerBase(data, dataLen, prevLayer, packet) {} - - /** - * A constructor for a new NDPNeighborSolicitationLayer object - * @param[in] code Code field - * @param[in] targetIP Target IP address for which the solicitation shall be created - */ - NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address &targetIP); - - /** - * A constructor for a new NDPNeighborSolicitationLayer object - * @param[in] code Code field - * @param[in] targetIP Target IP address for which the solicitation shall be created - * @param[in] srcMac Mac address which shall be put in the linklayer option - */ - NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address &targetIP, const MacAddress &srcMac); - - virtual ~NDPNeighborSolicitationLayer() {} - - /** - * @return Get the IP address specified as the target IP address in the solicitation message - */ - IPv6Address getTargetIP() const { return IPv6Address(getNdpHeader()->targetIP); }; - - /** - * Checks if the layer has a link layer address option set - * @return true if link layer address option is available, false otherwise - */ - bool hasLinkLayerAddress() const; - - /** - * Get the Link Layer Address - * @return Mac address which is specified in the link layer address option - */ - MacAddress getLinkLayerAddress() const; - - std::string toString() const; - -private: - void initLayer(uint8_t code, const IPv6Address &targetIP); - ndpneighborsolicitationhdr *getNdpHeader() const { return (ndpneighborsolicitationhdr *)m_Data; } - size_t getNdpHeaderLen() const { return sizeof(ndpneighborsolicitationhdr); }; -}; - -/** - * @class NDPNeighborAdvertisementLayer - * Represents a NDP Neighbor Advertisement protocol layer - */ -class NDPNeighborAdvertisementLayer : public NDPLayerBase -{ -public: - /** - * @struct ndpneighboradvertisementhdr - * Represents neighbor advertisement message format - */ -#pragma pack(push, 1) - struct ndpneighboradvertisementhdr : icmpv6hdr - { -#if (BYTE_ORDER == LITTLE_ENDIAN) - uint32_t - /** Unused field */ - reserved : 5, - /** Flag indicating that this entry should override the old one */ - override : 1, - /** Flag indicating that the advertisement was sent in response to a Neighbor Solicitation from the - Destination address */ - solicited : 1, - /** Flag indicating that the advertisement is sent by a router */ - router : 1, - /** Unused field */ - reserved2 : 24; -#else - uint32_t - /** Flag indicating that the advertisement is sent by a router */ - router : 1, - /** Flag indicating that the advertisement was sent in response to a Neighbor Solicitation from the - Destination address */ - solicited : 1, - /** Flag indicating that this entry should override the old one */ - override : 1, - /** Unused field */ - reserved : 29; -#endif - /** Target address - Either source address of advertisement or address for requested MAC */ - uint8_t targetIP[16]; - }; -#pragma pack(pop) - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - NDPNeighborAdvertisementLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : NDPLayerBase(data, dataLen, prevLayer, packet) {} - - /** - * A constructor that allocates a new NDP Advertisement Layer with target link-layer address option - * @param[in] code Code field - * @param[in] targetIP The target IP address from the Neighbor Solicitation message (solicited advertisements) or - * the address whose link-layer address has changed (unsolicited advertisement) - * @param[in] targetMac Adds the target link-layer address into the option field of the layer - * @param[in] routerFlag The router flag - * @param[in] unicastFlag The solicited flag - * @param[in] overrideFlag The override flag - */ - NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address &targetIP, const MacAddress &targetMac, - bool routerFlag, bool unicastFlag, bool overrideFlag); - - /** - * A constructor that allocates a new NDP Advertisement Layer - * @param code Code field - * @param targetIP The target IP address from the Neighbor Solicitation message (solicited advertisements) or the - * address whose link-layer address has changed (unsolicited advertisement) - * @param routerFlag The router flag - * @param unicastFlag The solicited flag - * @param overrideFlag The override flag - */ - NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address &targetIP, bool routerFlag, bool unicastFlag, - bool overrideFlag); - - virtual ~NDPNeighborAdvertisementLayer() {} - - /** - * @return Get the target MAC address - */ - MacAddress getTargetMac() const; - - /** - * @return Get the target IP address - */ - IPv6Address getTargetIP() const { return IPv6Address(getNdpHeader()->targetIP); } - - /** - * @return Get information if the target link-layer address was added in the option field of the header - */ - bool hasTargetMacInfo() const; - - /** - * @return Get the router flag - */ - bool getRouterFlag() const { return getNdpHeader()->router; } - - /** - * @return Get the unicast flag - */ - bool getUnicastFlag() const { return getNdpHeader()->solicited; } - - /** - * @return Get the override flag - */ - bool getOverrideFlag() const { return getNdpHeader()->override; } - - std::string toString() const; - -private: - void initLayer(uint8_t code, const IPv6Address &targetIP, bool routerFlag, bool unicastFlag, bool overrideFlag); - ndpneighboradvertisementhdr *getNdpHeader() const { return (ndpneighboradvertisementhdr *)m_Data; } - size_t getNdpHeaderLen() const { return sizeof(ndpneighboradvertisementhdr); }; -}; - -} // namespace pcpp -#endif /* PACKETPP_NDP_LAYER */ +#ifndef PACKETPP_NDP_LAYER +#define PACKETPP_NDP_LAYER + +#include "IcmpV6Layer.h" +#include "IpAddress.h" +#include "Layer.h" +#include "MacAddress.h" +#include "TLVData.h" + +#include + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + +/** + * An enum representing the available option types for Neighbor Discovery in IPv6 (see RFC 4861) + */ +enum class NDPNeighborOptionTypes : int +{ + NDP_OPTION_SOURCE_LINK_LAYER = 1, + NDP_OPTION_TARGET_LINK_LAYER = 2, + NDP_OPTION_PREFIX_INFORMATION = 3, + NDP_OPTION_REDIRECTED_HEADER = 4, + NDP_OPTION_MTU = 5, + NDP_OPTION_UNKNOWN = 255 +}; + +/** + * @class NdpOption + * A wrapper class for NDP options. This class does not create or modify NDP option records, but rather + * serves as a wrapper and provides useful methods for retrieving data from them + */ +class NdpOption : public TLVRecord +{ +public: + /** + * A c'tor for this class that gets a pointer to the option raw data (byte array) + * @param[in] optionRawData A pointer to the NDP option raw data + */ + explicit NdpOption(uint8_t *optionRawData) : TLVRecord(optionRawData) {} + + /** + * A d'tor for this class, currently does nothing + */ + ~NdpOption() {} + + /** + * @return NDP option type casted as pcpp::NDPNeighborOptionTypes enum. If the data is null a value + * of NDP_OPTION_UNKNOWN is returned + */ + NDPNeighborOptionTypes getNdpOptionType() const + { + if (m_Data == NULL) + return NDPNeighborOptionTypes::NDP_OPTION_UNKNOWN; + + return static_cast(m_Data->recordType); + } + + // implement abstract methods + + size_t getTotalSize() const + { + if (m_Data == NULL) + return (size_t)0; + + return (size_t)m_Data->recordLen * 8; + } + + size_t getDataSize() const + { + if (m_Data == NULL) + return 0; + + return (size_t)m_Data->recordLen * 8 - (2 * sizeof(uint8_t)); // length value is stored in units of 8 octets + } +}; + +/** + * @class NdpOptionBuilder + * A class for building NDP option records. This builder receives the NDP option parameters in its c'tor, + * builds the NDP option raw buffer and provides a build() method to get a NdpOption object out of it + */ +class NdpOptionBuilder : public TLVRecordBuilder +{ +public: + /** + * A c'tor for building NDP options which their value is a byte array. The NdpOption object can be later + * retrieved by calling build(). Each option is padded to have a 64-bit boundary. + * @param[in] optionType NDP option type + * @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in any + * way. + * @param[in] optionValueLen Option value length in bytes + */ + NdpOptionBuilder(NDPNeighborOptionTypes optionType, const uint8_t *optionValue, uint8_t optionValueLen) + : TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) {} + + /** + * Build the NdpOption object out of the parameters defined in the c'tor. Padding bytes are added to the + * option for option length with 64-bit boundaries. + * @return The NdpOption object + */ + NdpOption build() const; +}; + +/** + * @class NDPLayerBase + * Represents a base for NDP packet types + */ +class NDPLayerBase : public IcmpV6Layer +{ +public: + virtual ~NDPLayerBase() {} + + /** + * @return The number of NDP options in this layer + */ + size_t getNdpOptionCount() const; + + /** + * Get a NDP option by type. + * @param[in] option NDP option type + * @return An NdpOption object that contains the first option that matches this type, or logical NULL + * (NdpOption#isNull() == true) if no such option found + */ + NdpOption getNdpOption(NDPNeighborOptionTypes option) const; + + /** + * @return The first NDP option in the packet. If the current layer contains no options the returned value will + * contain a logical NULL (NdpOption#isNull() == true) + */ + NdpOption getFirstNdpOption() const; + + /** + * Get the NDP option that comes after a given option. If the given option was the last one, the + * returned value will contain a logical NULL (IdpOption#isNull() == true) + * @param[in] option An NDP option object that exists in the current layer + * @return A NdpOption object that contains the NDP option data that comes next, or logical NULL if the given + * NDP option: (1) was the last one; or (2) contains a logical NULL; or (3) doesn't belong to this packet + */ + NdpOption getNextNdpOption(NdpOption &option) const; + + /** + * Add a new NDP option at the end of the layer (after the last NDP option) + * @param[in] optionBuilder An NdpOptionBuilder object that contains the NDP option data to be added + * @return A NdpOption object that contains the newly added NDP option data or logical NULL + * (NdpOption#isNull() == true) if addition failed. In case of a failure a corresponding error message will be + * printed to log + */ + NdpOption addNdpOption(const NdpOptionBuilder &optionBuilder); + + /** + * Remove all NDP options from the layer + * @return True if options removed successfully or false if some error occurred (an appropriate error message will + * be printed to log) + */ + bool removeAllNdpOptions(); + +protected: + NDPLayerBase() = default; + + NDPLayerBase(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) + : IcmpV6Layer(data, dataLen, prevLayer, packet) {} + +private: + TLVRecordReader m_OptionReader; + + virtual size_t getNdpHeaderLen() const = 0; + virtual uint8_t *getNdpOptionsBasePtr() const { return m_Data + getNdpHeaderLen(); }; + NdpOption addNdpOptionAt(const NdpOptionBuilder &optionBuilder, int offset); +}; + +/** + * @class NDPNeighborSolicitationLayer + * Represents a NDP Neighbor Solicitation protocol layer + */ +class NDPNeighborSolicitationLayer : public NDPLayerBase +{ +public: + /** + * @struct ndpneighborsolicitationhdr + * Represents neighbor solicitation message format + */ +#pragma pack(push, 1) + struct ndpneighborsolicitationhdr : icmpv6hdr + { + /** Reserved */ + uint32_t reserved; + /** Target address - Target address of solicitation message */ + uint8_t targetIP[16]; + }; +#pragma pack(pop) + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + NDPNeighborSolicitationLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) + : NDPLayerBase(data, dataLen, prevLayer, packet) {} + + /** + * A constructor for a new NDPNeighborSolicitationLayer object + * @param[in] code Code field + * @param[in] targetIP Target IP address for which the solicitation shall be created + */ + NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address &targetIP); + + /** + * A constructor for a new NDPNeighborSolicitationLayer object + * @param[in] code Code field + * @param[in] targetIP Target IP address for which the solicitation shall be created + * @param[in] srcMac Mac address which shall be put in the linklayer option + */ + NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address &targetIP, const MacAddress &srcMac); + + virtual ~NDPNeighborSolicitationLayer() {} + + /** + * @return Get the IP address specified as the target IP address in the solicitation message + */ + IPv6Address getTargetIP() const { return IPv6Address(getNdpHeader()->targetIP); }; + + /** + * Checks if the layer has a link layer address option set + * @return true if link layer address option is available, false otherwise + */ + bool hasLinkLayerAddress() const; + + /** + * Get the Link Layer Address + * @return Mac address which is specified in the link layer address option + */ + MacAddress getLinkLayerAddress() const; + + std::string toString() const; + +private: + void initLayer(uint8_t code, const IPv6Address &targetIP); + ndpneighborsolicitationhdr *getNdpHeader() const { return (ndpneighborsolicitationhdr *)m_Data; } + size_t getNdpHeaderLen() const { return sizeof(ndpneighborsolicitationhdr); }; +}; + +/** + * @class NDPNeighborAdvertisementLayer + * Represents a NDP Neighbor Advertisement protocol layer + */ +class NDPNeighborAdvertisementLayer : public NDPLayerBase +{ +public: + /** + * @struct ndpneighboradvertisementhdr + * Represents neighbor advertisement message format + */ +#pragma pack(push, 1) + struct ndpneighboradvertisementhdr : icmpv6hdr + { +#if (BYTE_ORDER == LITTLE_ENDIAN) + uint32_t + /** Unused field */ + reserved : 5, + /** Flag indicating that this entry should override the old one */ + override : 1, + /** Flag indicating that the advertisement was sent in response to a Neighbor Solicitation from the + Destination address */ + solicited : 1, + /** Flag indicating that the advertisement is sent by a router */ + router : 1, + /** Unused field */ + reserved2 : 24; +#else + uint32_t + /** Flag indicating that the advertisement is sent by a router */ + router : 1, + /** Flag indicating that the advertisement was sent in response to a Neighbor Solicitation from the + Destination address */ + solicited : 1, + /** Flag indicating that this entry should override the old one */ + override : 1, + /** Unused field */ + reserved : 29; +#endif + /** Target address - Either source address of advertisement or address for requested MAC */ + uint8_t targetIP[16]; + }; +#pragma pack(pop) + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + NDPNeighborAdvertisementLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) + : NDPLayerBase(data, dataLen, prevLayer, packet) {} + + /** + * A constructor that allocates a new NDP Advertisement Layer with target link-layer address option + * @param[in] code Code field + * @param[in] targetIP The target IP address from the Neighbor Solicitation message (solicited advertisements) or + * the address whose link-layer address has changed (unsolicited advertisement) + * @param[in] targetMac Adds the target link-layer address into the option field of the layer + * @param[in] routerFlag The router flag + * @param[in] unicastFlag The solicited flag + * @param[in] overrideFlag The override flag + */ + NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address &targetIP, const MacAddress &targetMac, + bool routerFlag, bool unicastFlag, bool overrideFlag); + + /** + * A constructor that allocates a new NDP Advertisement Layer + * @param code Code field + * @param targetIP The target IP address from the Neighbor Solicitation message (solicited advertisements) or the + * address whose link-layer address has changed (unsolicited advertisement) + * @param routerFlag The router flag + * @param unicastFlag The solicited flag + * @param overrideFlag The override flag + */ + NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address &targetIP, bool routerFlag, bool unicastFlag, + bool overrideFlag); + + virtual ~NDPNeighborAdvertisementLayer() {} + + /** + * @return Get the target MAC address + */ + MacAddress getTargetMac() const; + + /** + * @return Get the target IP address + */ + IPv6Address getTargetIP() const { return IPv6Address(getNdpHeader()->targetIP); } + + /** + * @return Get information if the target link-layer address was added in the option field of the header + */ + bool hasTargetMacInfo() const; + + /** + * @return Get the router flag + */ + bool getRouterFlag() const { return getNdpHeader()->router; } + + /** + * @return Get the unicast flag + */ + bool getUnicastFlag() const { return getNdpHeader()->solicited; } + + /** + * @return Get the override flag + */ + bool getOverrideFlag() const { return getNdpHeader()->override; } + + std::string toString() const; + +private: + void initLayer(uint8_t code, const IPv6Address &targetIP, bool routerFlag, bool unicastFlag, bool overrideFlag); + ndpneighboradvertisementhdr *getNdpHeader() const { return (ndpneighboradvertisementhdr *)m_Data; } + size_t getNdpHeaderLen() const { return sizeof(ndpneighboradvertisementhdr); }; +}; + +} // namespace pcpp +#endif /* PACKETPP_NDP_LAYER */ diff --git a/Packet++/header/NullLoopbackLayer.h b/Packet++/header/NullLoopbackLayer.h index 160cf5308a..35deb858bf 100644 --- a/Packet++/header/NullLoopbackLayer.h +++ b/Packet++/header/NullLoopbackLayer.h @@ -1,92 +1,92 @@ -#ifndef PACKETPP_NULL_LOOPBACK_LAYER -#define PACKETPP_NULL_LOOPBACK_LAYER - -/// @file - -#include "Layer.h" - -namespace pcpp -{ - - /** IPv4 protocol **/ - #define PCPP_BSD_AF_INET 2 - /** XEROX NS protocols */ - #define PCPP_BSD_AF_NS 6 - /** ISO */ - #define PCPP_BSD_AF_ISO 7 - /** AppleTalk */ - #define PCPP_BSD_AF_APPLETALK 16 - /** IPX */ - #define PCPP_BSD_AF_IPX 23 - /** OpenBSD (and probably NetBSD), BSD/OS IPv6 */ - #define PCPP_BSD_AF_INET6_BSD 24 - /** FreeBSD IPv6 */ - #define PCPP_BSD_AF_INET6_FREEBSD 28 - /** Darwin IPv6 */ - #define PCPP_BSD_AF_INET6_DARWIN 30 - - /** - * @class NullLoopbackLayer - * Represents a NULL/Loopback layer - */ - class NullLoopbackLayer : public Layer - { - public: - /** A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - NullLoopbackLayer(uint8_t* data, size_t dataLen, Packet* packet) : Layer(data, dataLen, NULL, packet) { m_Protocol = NULL_LOOPBACK; } - - /** - * A constructor that allocates a new Null/Loopback header - * @param[in] family The family protocol to set - */ - explicit NullLoopbackLayer(uint32_t family); - - /** - * A destructor for this layer (does nothing) - */ - ~NullLoopbackLayer() {} - - /** - * @return The protocol family in this layer - */ - uint32_t getFamily() const; - - /** - * Set a protocol family - * @param[in] family The family protocol to set - */ - void setFamily(uint32_t family); - - - // 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(); - - /** - * @return Size of Null/Loopback header = 4B - */ - size_t getHeaderLen() const { return sizeof(uint32_t); } - - /** - * Does nothing for this layer - */ - void computeCalculateFields() {} - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - }; - -} // namespace pcpp - -#endif /* PACKETPP_NULL_LOOPBACK_LAYER */ +#ifndef PACKETPP_NULL_LOOPBACK_LAYER +#define PACKETPP_NULL_LOOPBACK_LAYER + +/// @file + +#include "Layer.h" + +namespace pcpp +{ + + /** IPv4 protocol **/ + #define PCPP_BSD_AF_INET 2 + /** XEROX NS protocols */ + #define PCPP_BSD_AF_NS 6 + /** ISO */ + #define PCPP_BSD_AF_ISO 7 + /** AppleTalk */ + #define PCPP_BSD_AF_APPLETALK 16 + /** IPX */ + #define PCPP_BSD_AF_IPX 23 + /** OpenBSD (and probably NetBSD), BSD/OS IPv6 */ + #define PCPP_BSD_AF_INET6_BSD 24 + /** FreeBSD IPv6 */ + #define PCPP_BSD_AF_INET6_FREEBSD 28 + /** Darwin IPv6 */ + #define PCPP_BSD_AF_INET6_DARWIN 30 + + /** + * @class NullLoopbackLayer + * Represents a NULL/Loopback layer + */ + class NullLoopbackLayer : public Layer + { + public: + /** A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + NullLoopbackLayer(uint8_t* data, size_t dataLen, Packet* packet) : Layer(data, dataLen, NULL, packet) { m_Protocol = NULL_LOOPBACK; } + + /** + * A constructor that allocates a new Null/Loopback header + * @param[in] family The family protocol to set + */ + explicit NullLoopbackLayer(uint32_t family); + + /** + * A destructor for this layer (does nothing) + */ + ~NullLoopbackLayer() {} + + /** + * @return The protocol family in this layer + */ + uint32_t getFamily() const; + + /** + * Set a protocol family + * @param[in] family The family protocol to set + */ + void setFamily(uint32_t family); + + + // 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(); + + /** + * @return Size of Null/Loopback header = 4B + */ + size_t getHeaderLen() const { return sizeof(uint32_t); } + + /** + * Does nothing for this layer + */ + void computeCalculateFields() {} + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } + }; + +} // namespace pcpp + +#endif /* PACKETPP_NULL_LOOPBACK_LAYER */ diff --git a/Packet++/header/Packet.h b/Packet++/header/Packet.h index 41df0585b7..6e10e662b2 100644 --- a/Packet++/header/Packet.h +++ b/Packet++/header/Packet.h @@ -1,392 +1,392 @@ -#ifndef PACKETPP_PACKET -#define PACKETPP_PACKET - -#include "RawPacket.h" -#include "Layer.h" -#include - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - - /** - * @class Packet - * This class represents a parsed packet. It contains the raw data (RawPacket instance), and a linked list of layers, each layer is a parsed - * protocol that this packet contains. The layers linked list is ordered where the first layer is the lowest in the packet (currently it's always - * Ethernet protocol as PcapPlusPlus supports only Ethernet packets), the next layer will be L2.5 or L3 (e.g VLAN, IPv4, IPv6, etc.), and so on. - * etc.), etc. The last layer in the linked list will be the highest in the packet. - * For example: for a standard HTTP request packet the layer will look like this: EthLayer -> IPv4Layer -> TcpLayer -> HttpRequestLayer
- * Packet instance isn't read only. The user can add or remove layers, update current layer, etc. - */ - class Packet - { - friend class Layer; - private: - RawPacket* m_RawPacket; - Layer* m_FirstLayer; - Layer* m_LastLayer; - uint64_t m_ProtocolTypes; - size_t m_MaxPacketLen; - bool m_FreeRawPacket; - bool m_CanReallocateData; - - public: - - /** - * A constructor for creating a new packet (with no layers). - * When using this constructor an empty raw buffer is allocated (with the size of maxPacketLen) and a new RawPacket is created - * @param[in] maxPacketLen The expected packet length in bytes - */ - explicit Packet(size_t maxPacketLen = 1); - - /** - * A constructor for creating a new packet with a buffer that is pre-allocated by the user. - * The packet is created empty (with no layers), which means the constructor doesn't parse the data in the buffer. - * Instead, all of the raw data of this packet it written to this buffer: whenever a layer is added, it's data is written to this buffer. - * The buffer isn't freed and it's content isn't erased when the packet object is deleted. - * This constructor is useful when you already have a memory buffer and you want to create packet data in it. - * @param[in] buffer A pointer to a pre-allocated memory buffer - * @param[in] bufferSize The size of the buffer - */ - Packet(uint8_t* buffer, size_t bufferSize); - - /** - * A constructor for creating a packet out of already allocated RawPacket. Very useful when parsing packets that came from the network. - * When using this constructor a pointer to the RawPacket is saved (data isn't copied) and the RawPacket is parsed, meaning all layers - * are created and linked to each other in the right order. In this overload of the constructor the user can specify whether to free - * the instance of raw packet when the Packet is free or not - * @param[in] rawPacket A pointer to the raw packet - * @param[in] freeRawPacket Optional parameter. A flag indicating if the destructor should also call the raw packet destructor or not. Default value is false - * @param[in] parseUntil Optional parameter. Parse the packet until you reach a certain protocol (inclusive). Can be useful for cases when you need to parse only up to a - * certain layer and want to avoid the performance impact and memory consumption of parsing the whole packet. Default value is ::UnknownProtocol which means don't take this - * parameter into account - * @param[in] parseUntilLayer Optional parameter. Parse the packet until you reach a certain layer in the OSI model (inclusive). Can be useful for cases when you need to - * parse only up to a certain OSI layer (for example transport layer) and want to avoid the performance impact and memory consumption of parsing the whole packet. - * Default value is ::OsiModelLayerUnknown which means don't take this parameter into account - */ - explicit Packet(RawPacket* rawPacket, bool freeRawPacket = false, ProtocolType parseUntil = UnknownProtocol, OsiModelLayer parseUntilLayer = OsiModelLayerUnknown); - - /** - * A constructor for creating a packet out of already allocated RawPacket. Very useful when parsing packets that came from the network. - * When using this constructor a pointer to the RawPacket is saved (data isn't copied) and the RawPacket is parsed, meaning all layers - * are created and linked to each other in the right order. In this overload of the constructor the user can specify whether to free - * the instance of raw packet when the Packet is free or not. This constructor should be used to parse the packet up to a certain layer - * @param[in] rawPacket A pointer to the raw packet - * @param[in] parseUntil Optional parameter. Parse the packet until you reach a certain protocol (inclusive). Can be useful for cases when you need to parse only up to a - * certain layer and want to avoid the performance impact and memory consumption of parsing the whole packet - */ - Packet(RawPacket* rawPacket, ProtocolType parseUntil); - - /** - * A constructor for creating a packet out of already allocated RawPacket. Very useful when parsing packets that came from the network. - * When using this constructor a pointer to the RawPacket is saved (data isn't copied) and the RawPacket is parsed, meaning all layers - * are created and linked to each other in the right order. In this overload of the constructor the user can specify whether to free - * the instance of raw packet when the Packet is free or not. . This constructor should be used to parse the packet up to a certain layer in the OSI model - * @param[in] rawPacket A pointer to the raw packet - * @param[in] parseUntilLayer Optional parameter. Parse the packet until you reach a certain layer in the OSI model (inclusive). Can be useful for cases when you need to - * parse only up to a certain OSI layer (for example transport layer) and want to avoid the performance impact and memory consumption of parsing the whole packet - */ - Packet(RawPacket* rawPacket, OsiModelLayer parseUntilLayer); - - /** - * A destructor for this class. Frees all layers allocated by this instance (Notice: it doesn't free layers that weren't allocated by this - * class, for example layers that were added by addLayer() or insertLayer() ). In addition it frees the raw packet if it was allocated by - * this instance (meaning if it was allocated by this instance constructor) - */ - virtual ~Packet() { destructPacketData(); } - - /** - * A copy constructor for this class. This copy constructor copies all the raw data and re-create all layers. So when the original Packet - * is being freed, no data will be lost in the copied instance - * @param[in] other The instance to copy from - */ - Packet(const Packet& other) { copyDataFrom(other); } - - /** - * Assignment operator overloading. It first frees all layers allocated by this instance (Notice: it doesn't free layers that weren't allocated by this - * class, for example layers that were added by addLayer() or insertLayer() ). In addition it frees the raw packet if it was allocated by - * this instance (meaning if it was allocated by this instance constructor). - * Afterwards it copies the data from the other packet in the same way used in the copy constructor. - * @param[in] other The instance to copy from - */ - Packet& operator=(const Packet& other); - - /** - * Get a pointer to the Packet's RawPacket - * @return A pointer to the Packet's RawPacket - */ - RawPacket* getRawPacket() const { return m_RawPacket; } - - /** - * Set a RawPacket and re-construct all packet layers - * @param[in] rawPacket Raw packet to set - * @param[in] freeRawPacket A flag indicating if the destructor should also call the raw packet destructor or not - * @param[in] parseUntil Parse the packet until it reaches this protocol. Can be useful for cases when you need to parse only up to a certain layer and want to avoid the - * performance impact and memory consumption of parsing the whole packet. Default value is ::UnknownProtocol which means don't take this parameter into account - * @param[in] parseUntilLayer Parse the packet until certain layer in OSI model. Can be useful for cases when you need to parse only up to a certain layer and want to avoid the - * performance impact and memory consumption of parsing the whole packet. Default value is ::OsiModelLayerUnknown which means don't take this parameter into account - */ - void setRawPacket(RawPacket* rawPacket, bool freeRawPacket, ProtocolType parseUntil = UnknownProtocol, OsiModelLayer parseUntilLayer = OsiModelLayerUnknown); - - /** - * Get a pointer to the Packet's RawPacket in a read-only manner - * @return A pointer to the Packet's RawPacket - */ - RawPacket* getRawPacketReadOnly() const { return m_RawPacket; } - - /** - * Get a pointer to the first (lowest) layer in the packet - * @return A pointer to the first (lowest) layer in the packet - */ - Layer* getFirstLayer() const { return m_FirstLayer; } - - /** - * Get a pointer to the last (highest) layer in the packet - * @return A pointer to the last (highest) layer in the packet - */ - Layer* getLastLayer() const { return m_LastLayer; } - - /** - * Add a new layer as the last layer in the packet. This method gets a pointer to the new layer as a parameter - * and attaches it to the packet. Notice after calling this method the input layer is attached to the packet so - * every change you make in it affect the packet; Also it cannot be attached to other packets - * @param[in] newLayer A pointer to the new layer to be added to the packet - * @param[in] ownInPacket If true, Packet fully owns newLayer, including memory deletion upon destruct. Default is false. - * @return True if everything went well or false otherwise (an appropriate error log message will be printed in - * such cases) - */ - bool addLayer(Layer* newLayer, bool ownInPacket = false) { return insertLayer(m_LastLayer, newLayer, ownInPacket); } - - /** - * Insert a new layer after an existing layer in the packet. This method gets a pointer to the new layer as a - * parameter and attaches it to the packet. Notice after calling this method the input layer is attached to the - * packet so every change you make in it affect the packet; Also it cannot be attached to other packets - * @param[in] prevLayer A pointer to an existing layer in the packet which the new layer should followed by. If - * this layer isn't attached to a packet and error will be printed to log and false will be returned - * @param[in] newLayer A pointer to the new layer to be added to the packet - * @param[in] ownInPacket If true, Packet fully owns newLayer, including memory deletion upon destruct. Default is false. - * @return True if everything went well or false otherwise (an appropriate error log message will be printed in - * such cases) - */ - bool insertLayer(Layer* prevLayer, Layer* newLayer, bool ownInPacket = false); - - - /** - * Remove an existing layer from the packet. The layer to removed is identified by its type (protocol). If the - * packet has multiple layers of the same type in the packet the user may specify the index of the layer to remove - * (the default index is 0 - remove the first layer of this type). If the layer was allocated during packet creation - * it will be deleted and any pointer to it will get invalid. However if the layer was allocated by the user and - * manually added to the packet it will simply get detached from the packet, meaning the pointer to it will stay - * valid and its data (that was removed from the packet) will be copied back to the layer. In that case it's - * the user's responsibility to delete the layer instance - * @param[in] layerType The layer type (protocol) to remove - * @param[in] index If there are multiple layers of the same type, indicate which instance to remove. The default - * value is 0, meaning remove the first layer of this type - * @return True if everything went well or false otherwise (an appropriate error log message will be printed in - * such cases) - */ - bool removeLayer(ProtocolType layerType, int index = 0); - - /** - * Remove the first layer in the packet. The layer will be deleted if it was allocated during packet creation, or detached - * if was allocated outside of the packet. Please refer to removeLayer() to get more info - * @return True if layer removed successfully, or false if removing the layer failed or if there are no layers in the - * packet. In any case of failure an appropriate error log message will be printed - */ - bool removeFirstLayer(); - - /** - * Remove the last layer in the packet. The layer will be deleted if it was allocated during packet creation, or detached - * if was allocated outside of the packet. Please refer to removeLayer() to get more info - * @return True if layer removed successfully, or false if removing the layer failed or if there are no layers in the - * packet. In any case of failure an appropriate error log message will be printed - */ - bool removeLastLayer(); - - /** - * Remove all layers that come after a certain layer. All layers removed will be deleted if they were allocated during - * packet creation or detached if were allocated outside of the packet, please refer to removeLayer() to get more info - * @param[in] layer A pointer to the layer to begin removing from. Please note this layer will not be removed, only the - * layers that come after it will be removed. Also, if removal of one layer failed, the method will return immediately and - * the following layers won't be deleted - * @return True if all layers were removed successfully, or false if failed to remove at least one layer. In any case of - * failure an appropriate error log message will be printed - */ - bool removeAllLayersAfter(Layer* layer); - - /** - * Detach a layer from the packet. Detaching means the layer instance will not be deleted, but rather separated from the - * packet - e.g it will be removed from the layer chain of the packet and its data will be copied from the packet buffer - * into an internal layer buffer. After a layer is detached, it can be added into another packet (but it's impossible to - * attach a layer to multiple packets in the same time). After layer is detached, it's the user's responsibility to - * delete it when it's not needed anymore - * @param[in] layerType The layer type (protocol) to detach from the packet - * @param[in] index If there are multiple layers of the same type, indicate which instance to detach. The default - * value is 0, meaning detach the first layer of this type - * @return A pointer to the detached layer or NULL if detaching process failed. In any case of failure an - * appropriate error log message will be printed - */ - Layer* detachLayer(ProtocolType layerType, int index = 0); - - /** - * Detach a layer from the packet. Detaching means the layer instance will not be deleted, but rather separated from the - * packet - e.g it will be removed from the layer chain of the packet and its data will be copied from the packet buffer - * into an internal layer buffer. After a layer is detached, it can be added into another packet (but it's impossible to - * attach a layer to multiple packets at the same time). After layer is detached, it's the user's responsibility to - * delete it when it's not needed anymore - * @param[in] layer A pointer to the layer to detach - * @return True if the layer was detached successfully, or false if something went wrong. In any case of failure an - * appropriate error log message will be printed - */ - bool detachLayer(Layer* layer) { return removeLayer(layer, false); } - - /** - * Get a pointer to the layer of a certain type (protocol). This method goes through the layers and returns a layer - * that matches the give protocol type - * @param[in] layerType The layer type (protocol) to fetch - * @param[in] index If there are multiple layers of the same type, indicate which instance to fetch. The default - * value is 0, meaning fetch the first layer of this type - * @return A pointer to the layer or NULL if no such layer was found - */ - Layer* getLayerOfType(ProtocolType layerType, int index = 0) const; - - /** - * A templated method to get a layer of a certain type (protocol). If no layer of such type is found, NULL is returned - * @param[in] reverseOrder The optional parameter that indicates that the lookup should run in reverse order, the default value is false - * @return A pointer to the layer of the requested type, NULL if not found - */ - template - TLayer* getLayerOfType(bool reverseOrder = false) const; - - /** - * A templated method to get the first layer of a certain type (protocol), start searching from a certain layer. - * For example: if a packet looks like: EthLayer -> VlanLayer(1) -> VlanLayer(2) -> VlanLayer(3) -> IPv4Layer - * and the user put VlanLayer(2) as a parameter and wishes to search for a VlanLayer, VlanLayer(3) will be returned - * If no layer of such type is found, NULL is returned - * @param[in] startLayer A pointer to the layer to start search from - * @return A pointer to the layer of the requested type, NULL if not found - */ - template - TLayer* getNextLayerOfType(Layer* startLayer) const; - - /** - * A templated method to get the first layer of a certain type (protocol), start searching from a certain layer. - * For example: if a packet looks like: EthLayer -> VlanLayer(1) -> VlanLayer(2) -> VlanLayer(3) -> IPv4Layer - * and the user put VlanLayer(2) as a parameter and wishes to search for a VlanLayer, VlanLayer(1) will be returned - * If no layer of such type is found, NULL is returned - * @param[in] startLayer A pointer to the layer to start search from - * @return A pointer to the layer of the requested type, NULL if not found - */ - template - TLayer* getPrevLayerOfType(Layer* startLayer) const; - - /** - * Check whether the packet contains a certain protocol - * @param[in] protocolType The protocol type to search - * @return True if the packet contains the protocol, false otherwise - */ - bool isPacketOfType(ProtocolType protocolType) const { return m_ProtocolTypes & protocolType; } - - /** - * Each layer can have fields that can be calculate automatically from other fields using Layer#computeCalculateFields(). This method forces all layers to calculate these - * fields values - */ - void computeCalculateFields(); - - /** - * Each layer can print a string representation of the layer most important data using Layer#toString(). This method aggregates this string from all layers and - * print it to a complete string containing all packet's relevant data - * @param[in] timeAsLocalTime Print time as local time or GMT. Default (true value) is local time, for GMT set to false - * @return A string containing most relevant data from all layers (looks like the packet description in Wireshark) - */ - std::string toString(bool timeAsLocalTime = true) const; - - /** - * Similar to toString(), but instead of one string it outputs a list of strings, one string for every layer - * @param[out] result A string vector that will contain all strings - * @param[in] timeAsLocalTime Print time as local time or GMT. Default (true value) is local time, for GMT set to false - */ - void toStringList(std::vector& result, bool timeAsLocalTime = true) const; - - private: - void copyDataFrom(const Packet& other); - - void destructPacketData(); - - bool extendLayer(Layer* layer, int offsetInLayer, size_t numOfBytesToExtend); - bool shortenLayer(Layer* layer, int offsetInLayer, size_t numOfBytesToShorten); - - void reallocateRawData(size_t newSize); - - bool removeLayer(Layer* layer, bool tryToDelete); - - std::string printPacketInfo(bool timeAsLocalTime) const; - - Layer* createFirstLayer(LinkLayerType linkType); - }; // class Packet - - - // implementation of inline methods - - template - TLayer* Packet::getLayerOfType(bool reverse) const - { - if (!reverse) - { - if (dynamic_cast(getFirstLayer()) != NULL) - return dynamic_cast(getFirstLayer()); - - return getNextLayerOfType(getFirstLayer()); - } - - // lookup in reverse order - if (dynamic_cast(getLastLayer()) != NULL) - return dynamic_cast(getLastLayer()); - - return getPrevLayerOfType(getLastLayer()); - } - - template - TLayer* Packet::getNextLayerOfType(Layer* curLayer) const - { - if (curLayer == NULL) - return NULL; - - curLayer = curLayer->getNextLayer(); - while ((curLayer != NULL) && (dynamic_cast(curLayer) == NULL)) - { - curLayer = curLayer->getNextLayer(); - } - - return dynamic_cast(curLayer); - } - - template - TLayer* Packet::getPrevLayerOfType(Layer* curLayer) const - { - if (curLayer == NULL) - return NULL; - - curLayer = curLayer->getPrevLayer(); - while (curLayer != NULL && dynamic_cast(curLayer) == NULL) - { - curLayer = curLayer->getPrevLayer(); - } - - return dynamic_cast(curLayer); - } - -} // namespace pcpp - -inline std::ostream& operator<<(std::ostream& os, const pcpp::Packet& packet) -{ - os << packet.toString(); - return os; -} - -#endif /* PACKETPP_PACKET */ +#ifndef PACKETPP_PACKET +#define PACKETPP_PACKET + +#include "RawPacket.h" +#include "Layer.h" +#include + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + + /** + * @class Packet + * This class represents a parsed packet. It contains the raw data (RawPacket instance), and a linked list of layers, each layer is a parsed + * protocol that this packet contains. The layers linked list is ordered where the first layer is the lowest in the packet (currently it's always + * Ethernet protocol as PcapPlusPlus supports only Ethernet packets), the next layer will be L2.5 or L3 (e.g VLAN, IPv4, IPv6, etc.), and so on. + * etc.), etc. The last layer in the linked list will be the highest in the packet. + * For example: for a standard HTTP request packet the layer will look like this: EthLayer -> IPv4Layer -> TcpLayer -> HttpRequestLayer
+ * Packet instance isn't read only. The user can add or remove layers, update current layer, etc. + */ + class Packet + { + friend class Layer; + private: + RawPacket* m_RawPacket; + Layer* m_FirstLayer; + Layer* m_LastLayer; + uint64_t m_ProtocolTypes; + size_t m_MaxPacketLen; + bool m_FreeRawPacket; + bool m_CanReallocateData; + + public: + + /** + * A constructor for creating a new packet (with no layers). + * When using this constructor an empty raw buffer is allocated (with the size of maxPacketLen) and a new RawPacket is created + * @param[in] maxPacketLen The expected packet length in bytes + */ + explicit Packet(size_t maxPacketLen = 1); + + /** + * A constructor for creating a new packet with a buffer that is pre-allocated by the user. + * The packet is created empty (with no layers), which means the constructor doesn't parse the data in the buffer. + * Instead, all of the raw data of this packet it written to this buffer: whenever a layer is added, it's data is written to this buffer. + * The buffer isn't freed and it's content isn't erased when the packet object is deleted. + * This constructor is useful when you already have a memory buffer and you want to create packet data in it. + * @param[in] buffer A pointer to a pre-allocated memory buffer + * @param[in] bufferSize The size of the buffer + */ + Packet(uint8_t* buffer, size_t bufferSize); + + /** + * A constructor for creating a packet out of already allocated RawPacket. Very useful when parsing packets that came from the network. + * When using this constructor a pointer to the RawPacket is saved (data isn't copied) and the RawPacket is parsed, meaning all layers + * are created and linked to each other in the right order. In this overload of the constructor the user can specify whether to free + * the instance of raw packet when the Packet is free or not + * @param[in] rawPacket A pointer to the raw packet + * @param[in] freeRawPacket Optional parameter. A flag indicating if the destructor should also call the raw packet destructor or not. Default value is false + * @param[in] parseUntil Optional parameter. Parse the packet until you reach a certain protocol (inclusive). Can be useful for cases when you need to parse only up to a + * certain layer and want to avoid the performance impact and memory consumption of parsing the whole packet. Default value is ::UnknownProtocol which means don't take this + * parameter into account + * @param[in] parseUntilLayer Optional parameter. Parse the packet until you reach a certain layer in the OSI model (inclusive). Can be useful for cases when you need to + * parse only up to a certain OSI layer (for example transport layer) and want to avoid the performance impact and memory consumption of parsing the whole packet. + * Default value is ::OsiModelLayerUnknown which means don't take this parameter into account + */ + explicit Packet(RawPacket* rawPacket, bool freeRawPacket = false, ProtocolType parseUntil = UnknownProtocol, OsiModelLayer parseUntilLayer = OsiModelLayerUnknown); + + /** + * A constructor for creating a packet out of already allocated RawPacket. Very useful when parsing packets that came from the network. + * When using this constructor a pointer to the RawPacket is saved (data isn't copied) and the RawPacket is parsed, meaning all layers + * are created and linked to each other in the right order. In this overload of the constructor the user can specify whether to free + * the instance of raw packet when the Packet is free or not. This constructor should be used to parse the packet up to a certain layer + * @param[in] rawPacket A pointer to the raw packet + * @param[in] parseUntil Optional parameter. Parse the packet until you reach a certain protocol (inclusive). Can be useful for cases when you need to parse only up to a + * certain layer and want to avoid the performance impact and memory consumption of parsing the whole packet + */ + Packet(RawPacket* rawPacket, ProtocolType parseUntil); + + /** + * A constructor for creating a packet out of already allocated RawPacket. Very useful when parsing packets that came from the network. + * When using this constructor a pointer to the RawPacket is saved (data isn't copied) and the RawPacket is parsed, meaning all layers + * are created and linked to each other in the right order. In this overload of the constructor the user can specify whether to free + * the instance of raw packet when the Packet is free or not. . This constructor should be used to parse the packet up to a certain layer in the OSI model + * @param[in] rawPacket A pointer to the raw packet + * @param[in] parseUntilLayer Optional parameter. Parse the packet until you reach a certain layer in the OSI model (inclusive). Can be useful for cases when you need to + * parse only up to a certain OSI layer (for example transport layer) and want to avoid the performance impact and memory consumption of parsing the whole packet + */ + Packet(RawPacket* rawPacket, OsiModelLayer parseUntilLayer); + + /** + * A destructor for this class. Frees all layers allocated by this instance (Notice: it doesn't free layers that weren't allocated by this + * class, for example layers that were added by addLayer() or insertLayer() ). In addition it frees the raw packet if it was allocated by + * this instance (meaning if it was allocated by this instance constructor) + */ + virtual ~Packet() { destructPacketData(); } + + /** + * A copy constructor for this class. This copy constructor copies all the raw data and re-create all layers. So when the original Packet + * is being freed, no data will be lost in the copied instance + * @param[in] other The instance to copy from + */ + Packet(const Packet& other) { copyDataFrom(other); } + + /** + * Assignment operator overloading. It first frees all layers allocated by this instance (Notice: it doesn't free layers that weren't allocated by this + * class, for example layers that were added by addLayer() or insertLayer() ). In addition it frees the raw packet if it was allocated by + * this instance (meaning if it was allocated by this instance constructor). + * Afterwards it copies the data from the other packet in the same way used in the copy constructor. + * @param[in] other The instance to copy from + */ + Packet& operator=(const Packet& other); + + /** + * Get a pointer to the Packet's RawPacket + * @return A pointer to the Packet's RawPacket + */ + RawPacket* getRawPacket() const { return m_RawPacket; } + + /** + * Set a RawPacket and re-construct all packet layers + * @param[in] rawPacket Raw packet to set + * @param[in] freeRawPacket A flag indicating if the destructor should also call the raw packet destructor or not + * @param[in] parseUntil Parse the packet until it reaches this protocol. Can be useful for cases when you need to parse only up to a certain layer and want to avoid the + * performance impact and memory consumption of parsing the whole packet. Default value is ::UnknownProtocol which means don't take this parameter into account + * @param[in] parseUntilLayer Parse the packet until certain layer in OSI model. Can be useful for cases when you need to parse only up to a certain layer and want to avoid the + * performance impact and memory consumption of parsing the whole packet. Default value is ::OsiModelLayerUnknown which means don't take this parameter into account + */ + void setRawPacket(RawPacket* rawPacket, bool freeRawPacket, ProtocolType parseUntil = UnknownProtocol, OsiModelLayer parseUntilLayer = OsiModelLayerUnknown); + + /** + * Get a pointer to the Packet's RawPacket in a read-only manner + * @return A pointer to the Packet's RawPacket + */ + RawPacket* getRawPacketReadOnly() const { return m_RawPacket; } + + /** + * Get a pointer to the first (lowest) layer in the packet + * @return A pointer to the first (lowest) layer in the packet + */ + Layer* getFirstLayer() const { return m_FirstLayer; } + + /** + * Get a pointer to the last (highest) layer in the packet + * @return A pointer to the last (highest) layer in the packet + */ + Layer* getLastLayer() const { return m_LastLayer; } + + /** + * Add a new layer as the last layer in the packet. This method gets a pointer to the new layer as a parameter + * and attaches it to the packet. Notice after calling this method the input layer is attached to the packet so + * every change you make in it affect the packet; Also it cannot be attached to other packets + * @param[in] newLayer A pointer to the new layer to be added to the packet + * @param[in] ownInPacket If true, Packet fully owns newLayer, including memory deletion upon destruct. Default is false. + * @return True if everything went well or false otherwise (an appropriate error log message will be printed in + * such cases) + */ + bool addLayer(Layer* newLayer, bool ownInPacket = false) { return insertLayer(m_LastLayer, newLayer, ownInPacket); } + + /** + * Insert a new layer after an existing layer in the packet. This method gets a pointer to the new layer as a + * parameter and attaches it to the packet. Notice after calling this method the input layer is attached to the + * packet so every change you make in it affect the packet; Also it cannot be attached to other packets + * @param[in] prevLayer A pointer to an existing layer in the packet which the new layer should followed by. If + * this layer isn't attached to a packet and error will be printed to log and false will be returned + * @param[in] newLayer A pointer to the new layer to be added to the packet + * @param[in] ownInPacket If true, Packet fully owns newLayer, including memory deletion upon destruct. Default is false. + * @return True if everything went well or false otherwise (an appropriate error log message will be printed in + * such cases) + */ + bool insertLayer(Layer* prevLayer, Layer* newLayer, bool ownInPacket = false); + + + /** + * Remove an existing layer from the packet. The layer to removed is identified by its type (protocol). If the + * packet has multiple layers of the same type in the packet the user may specify the index of the layer to remove + * (the default index is 0 - remove the first layer of this type). If the layer was allocated during packet creation + * it will be deleted and any pointer to it will get invalid. However if the layer was allocated by the user and + * manually added to the packet it will simply get detached from the packet, meaning the pointer to it will stay + * valid and its data (that was removed from the packet) will be copied back to the layer. In that case it's + * the user's responsibility to delete the layer instance + * @param[in] layerType The layer type (protocol) to remove + * @param[in] index If there are multiple layers of the same type, indicate which instance to remove. The default + * value is 0, meaning remove the first layer of this type + * @return True if everything went well or false otherwise (an appropriate error log message will be printed in + * such cases) + */ + bool removeLayer(ProtocolType layerType, int index = 0); + + /** + * Remove the first layer in the packet. The layer will be deleted if it was allocated during packet creation, or detached + * if was allocated outside of the packet. Please refer to removeLayer() to get more info + * @return True if layer removed successfully, or false if removing the layer failed or if there are no layers in the + * packet. In any case of failure an appropriate error log message will be printed + */ + bool removeFirstLayer(); + + /** + * Remove the last layer in the packet. The layer will be deleted if it was allocated during packet creation, or detached + * if was allocated outside of the packet. Please refer to removeLayer() to get more info + * @return True if layer removed successfully, or false if removing the layer failed or if there are no layers in the + * packet. In any case of failure an appropriate error log message will be printed + */ + bool removeLastLayer(); + + /** + * Remove all layers that come after a certain layer. All layers removed will be deleted if they were allocated during + * packet creation or detached if were allocated outside of the packet, please refer to removeLayer() to get more info + * @param[in] layer A pointer to the layer to begin removing from. Please note this layer will not be removed, only the + * layers that come after it will be removed. Also, if removal of one layer failed, the method will return immediately and + * the following layers won't be deleted + * @return True if all layers were removed successfully, or false if failed to remove at least one layer. In any case of + * failure an appropriate error log message will be printed + */ + bool removeAllLayersAfter(Layer* layer); + + /** + * Detach a layer from the packet. Detaching means the layer instance will not be deleted, but rather separated from the + * packet - e.g it will be removed from the layer chain of the packet and its data will be copied from the packet buffer + * into an internal layer buffer. After a layer is detached, it can be added into another packet (but it's impossible to + * attach a layer to multiple packets in the same time). After layer is detached, it's the user's responsibility to + * delete it when it's not needed anymore + * @param[in] layerType The layer type (protocol) to detach from the packet + * @param[in] index If there are multiple layers of the same type, indicate which instance to detach. The default + * value is 0, meaning detach the first layer of this type + * @return A pointer to the detached layer or NULL if detaching process failed. In any case of failure an + * appropriate error log message will be printed + */ + Layer* detachLayer(ProtocolType layerType, int index = 0); + + /** + * Detach a layer from the packet. Detaching means the layer instance will not be deleted, but rather separated from the + * packet - e.g it will be removed from the layer chain of the packet and its data will be copied from the packet buffer + * into an internal layer buffer. After a layer is detached, it can be added into another packet (but it's impossible to + * attach a layer to multiple packets at the same time). After layer is detached, it's the user's responsibility to + * delete it when it's not needed anymore + * @param[in] layer A pointer to the layer to detach + * @return True if the layer was detached successfully, or false if something went wrong. In any case of failure an + * appropriate error log message will be printed + */ + bool detachLayer(Layer* layer) { return removeLayer(layer, false); } + + /** + * Get a pointer to the layer of a certain type (protocol). This method goes through the layers and returns a layer + * that matches the give protocol type + * @param[in] layerType The layer type (protocol) to fetch + * @param[in] index If there are multiple layers of the same type, indicate which instance to fetch. The default + * value is 0, meaning fetch the first layer of this type + * @return A pointer to the layer or NULL if no such layer was found + */ + Layer* getLayerOfType(ProtocolType layerType, int index = 0) const; + + /** + * A templated method to get a layer of a certain type (protocol). If no layer of such type is found, NULL is returned + * @param[in] reverseOrder The optional parameter that indicates that the lookup should run in reverse order, the default value is false + * @return A pointer to the layer of the requested type, NULL if not found + */ + template + TLayer* getLayerOfType(bool reverseOrder = false) const; + + /** + * A templated method to get the first layer of a certain type (protocol), start searching from a certain layer. + * For example: if a packet looks like: EthLayer -> VlanLayer(1) -> VlanLayer(2) -> VlanLayer(3) -> IPv4Layer + * and the user put VlanLayer(2) as a parameter and wishes to search for a VlanLayer, VlanLayer(3) will be returned + * If no layer of such type is found, NULL is returned + * @param[in] startLayer A pointer to the layer to start search from + * @return A pointer to the layer of the requested type, NULL if not found + */ + template + TLayer* getNextLayerOfType(Layer* startLayer) const; + + /** + * A templated method to get the first layer of a certain type (protocol), start searching from a certain layer. + * For example: if a packet looks like: EthLayer -> VlanLayer(1) -> VlanLayer(2) -> VlanLayer(3) -> IPv4Layer + * and the user put VlanLayer(2) as a parameter and wishes to search for a VlanLayer, VlanLayer(1) will be returned + * If no layer of such type is found, NULL is returned + * @param[in] startLayer A pointer to the layer to start search from + * @return A pointer to the layer of the requested type, NULL if not found + */ + template + TLayer* getPrevLayerOfType(Layer* startLayer) const; + + /** + * Check whether the packet contains a certain protocol + * @param[in] protocolType The protocol type to search + * @return True if the packet contains the protocol, false otherwise + */ + bool isPacketOfType(ProtocolType protocolType) const { return m_ProtocolTypes & protocolType; } + + /** + * Each layer can have fields that can be calculate automatically from other fields using Layer#computeCalculateFields(). This method forces all layers to calculate these + * fields values + */ + void computeCalculateFields(); + + /** + * Each layer can print a string representation of the layer most important data using Layer#toString(). This method aggregates this string from all layers and + * print it to a complete string containing all packet's relevant data + * @param[in] timeAsLocalTime Print time as local time or GMT. Default (true value) is local time, for GMT set to false + * @return A string containing most relevant data from all layers (looks like the packet description in Wireshark) + */ + std::string toString(bool timeAsLocalTime = true) const; + + /** + * Similar to toString(), but instead of one string it outputs a list of strings, one string for every layer + * @param[out] result A string vector that will contain all strings + * @param[in] timeAsLocalTime Print time as local time or GMT. Default (true value) is local time, for GMT set to false + */ + void toStringList(std::vector& result, bool timeAsLocalTime = true) const; + + private: + void copyDataFrom(const Packet& other); + + void destructPacketData(); + + bool extendLayer(Layer* layer, int offsetInLayer, size_t numOfBytesToExtend); + bool shortenLayer(Layer* layer, int offsetInLayer, size_t numOfBytesToShorten); + + void reallocateRawData(size_t newSize); + + bool removeLayer(Layer* layer, bool tryToDelete); + + std::string printPacketInfo(bool timeAsLocalTime) const; + + Layer* createFirstLayer(LinkLayerType linkType); + }; // class Packet + + + // implementation of inline methods + + template + TLayer* Packet::getLayerOfType(bool reverse) const + { + if (!reverse) + { + if (dynamic_cast(getFirstLayer()) != NULL) + return dynamic_cast(getFirstLayer()); + + return getNextLayerOfType(getFirstLayer()); + } + + // lookup in reverse order + if (dynamic_cast(getLastLayer()) != NULL) + return dynamic_cast(getLastLayer()); + + return getPrevLayerOfType(getLastLayer()); + } + + template + TLayer* Packet::getNextLayerOfType(Layer* curLayer) const + { + if (curLayer == NULL) + return NULL; + + curLayer = curLayer->getNextLayer(); + while ((curLayer != NULL) && (dynamic_cast(curLayer) == NULL)) + { + curLayer = curLayer->getNextLayer(); + } + + return dynamic_cast(curLayer); + } + + template + TLayer* Packet::getPrevLayerOfType(Layer* curLayer) const + { + if (curLayer == NULL) + return NULL; + + curLayer = curLayer->getPrevLayer(); + while (curLayer != NULL && dynamic_cast(curLayer) == NULL) + { + curLayer = curLayer->getPrevLayer(); + } + + return dynamic_cast(curLayer); + } + +} // namespace pcpp + +inline std::ostream& operator<<(std::ostream& os, const pcpp::Packet& packet) +{ + os << packet.toString(); + return os; +} + +#endif /* PACKETPP_PACKET */ diff --git a/Packet++/header/PacketTrailerLayer.h b/Packet++/header/PacketTrailerLayer.h index 8cd2c4ed7d..12f12ddead 100644 --- a/Packet++/header/PacketTrailerLayer.h +++ b/Packet++/header/PacketTrailerLayer.h @@ -1,84 +1,84 @@ -#ifndef PACKETPP_PACKET_TRAILER_LAYER -#define PACKETPP_PACKET_TRAILER_LAYER - -/// @file - -#include "Layer.h" - -namespace pcpp -{ - /** - * @class PacketTrailerLayer - * A class for representing packet tailer (a.k.a footer or padding) which refers to supplemental data placed at the end of a block of data - * being stored or transmitted, which may contain information for the handling of the data block, or just mark its end - * (taken from Wikipedia: https://en.wikipedia.org/wiki/Trailer_(computing) ) - * - * There are various reasons for adding a packet trailer, one of the most famous is FCS (Frame check sequence) which refers to the extra - * error-detecting code added to a frame. Another usage is padding which means adding data to reach a minimum required packet length. - * - * Although this layer inherits from the Layer class, it is not a standard layer in the sense that it can't be constructed by the user. - * This layer may be only be constructed in the Packet class, in the process of parsing the packet and creating the layers; if at the end - * of the parsing process there is data left that is not allocated to any layer, it's assumed to be the packet trailer and an instance of - * this class is created. This means this layer can only exist as the last layer in a packet, if a packet trailer indeed exists. - * - * No layer can be added by the user after this layer (trying to do that will result with an error). - * - * This layer can be removed by the user or extended/shortened, as any layer. - * - * It also contains method to extract the trailer data - */ - class PacketTrailerLayer : public Layer - { - public: - /** A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - PacketTrailerLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = PacketTrailer; } - - ~PacketTrailerLayer() {} - - /** - * Get a pointer to the trailer data - * @return A pointer to the trailer data - */ - uint8_t* getTrailerData() const { return m_Data; } - - /** - * @return Trailer data as hex string - */ - std::string getTrailerDataAsHexString() const; - - /** - * Get the trailer data length - * @return The trailer data length in bytes - */ - size_t getTrailerLen() const { return m_DataLen; } - - // implement abstract methods - - /** - * Does nothing for this layer (PacketTrailerLayer is always last) - */ - void parseNextLayer() {} - - /** - * @return trailer data length in bytes - */ - size_t getHeaderLen() const { return m_DataLen; } - - /** - * Does nothing for this layer - */ - void computeCalculateFields() {} - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - }; - -} - -#endif // PACKETPP_PACKET_TRAILER_LAYER +#ifndef PACKETPP_PACKET_TRAILER_LAYER +#define PACKETPP_PACKET_TRAILER_LAYER + +/// @file + +#include "Layer.h" + +namespace pcpp +{ + /** + * @class PacketTrailerLayer + * A class for representing packet tailer (a.k.a footer or padding) which refers to supplemental data placed at the end of a block of data + * being stored or transmitted, which may contain information for the handling of the data block, or just mark its end + * (taken from Wikipedia: https://en.wikipedia.org/wiki/Trailer_(computing) ) + * + * There are various reasons for adding a packet trailer, one of the most famous is FCS (Frame check sequence) which refers to the extra + * error-detecting code added to a frame. Another usage is padding which means adding data to reach a minimum required packet length. + * + * Although this layer inherits from the Layer class, it is not a standard layer in the sense that it can't be constructed by the user. + * This layer may be only be constructed in the Packet class, in the process of parsing the packet and creating the layers; if at the end + * of the parsing process there is data left that is not allocated to any layer, it's assumed to be the packet trailer and an instance of + * this class is created. This means this layer can only exist as the last layer in a packet, if a packet trailer indeed exists. + * + * No layer can be added by the user after this layer (trying to do that will result with an error). + * + * This layer can be removed by the user or extended/shortened, as any layer. + * + * It also contains method to extract the trailer data + */ + class PacketTrailerLayer : public Layer + { + public: + /** A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + PacketTrailerLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = PacketTrailer; } + + ~PacketTrailerLayer() {} + + /** + * Get a pointer to the trailer data + * @return A pointer to the trailer data + */ + uint8_t* getTrailerData() const { return m_Data; } + + /** + * @return Trailer data as hex string + */ + std::string getTrailerDataAsHexString() const; + + /** + * Get the trailer data length + * @return The trailer data length in bytes + */ + size_t getTrailerLen() const { return m_DataLen; } + + // implement abstract methods + + /** + * Does nothing for this layer (PacketTrailerLayer is always last) + */ + void parseNextLayer() {} + + /** + * @return trailer data length in bytes + */ + size_t getHeaderLen() const { return m_DataLen; } + + /** + * Does nothing for this layer + */ + void computeCalculateFields() {} + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } + }; + +} + +#endif // PACKETPP_PACKET_TRAILER_LAYER diff --git a/Packet++/header/PacketUtils.h b/Packet++/header/PacketUtils.h index c0d70150f9..76f2685f0e 100644 --- a/Packet++/header/PacketUtils.h +++ b/Packet++/header/PacketUtils.h @@ -1,76 +1,76 @@ -#ifndef PACKETPP_PACKET_UTILS -#define PACKETPP_PACKET_UTILS - -#include "Packet.h" - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - /** - * A struct that represent a single buffer - */ - template - struct ScalarBuffer - { - /** - * The pointer to the buffer - */ - T* buffer; - - /** - * Buffer length - */ - size_t len; - }; - - /** - * Computes the checksum for a vector of buffers - * @param[in] vec The vector of buffers - * @param[in] vecSize Number of ScalarBuffers in vector - * @return The checksum result - */ - uint16_t computeChecksum(ScalarBuffer vec[], size_t vecSize); - - /** - * Computes Fowler-Noll-Vo (FNV-1) 32bit hash function on an array of byte buffers. The hash is calculated on each - * byte in each byte buffer, as if all byte buffers were one long byte buffer - * @param[in] vec An array of byte buffers (ScalarBuffer of type uint8_t) - * @param[in] vecSize The length of vec - * @return The 32bit hash value - */ - uint32_t fnvHash(ScalarBuffer vec[], size_t vecSize); - - /** - * Computes Fowler-Noll-Vo (FNV-1) 32bit hash function on a byte buffer - * @param[in] buffer The byte buffer - * @param[in] bufSize The size of the byte buffer - * @return The 32bit hash value - */ - uint32_t fnvHash(uint8_t* buffer, size_t bufSize); - - /** - * A method that is given a packet and calculates a hash value by the packet's 5-tuple. Supports IPv4, IPv6, - * TCP and UDP. For packets which doesn't have 5-tuple (for example: packets which aren't IPv4/6 or aren't - * TCP/UDP) the value of 0 will be returned - * @param[in] packet The packet to calculate hash for - * @param[in] directionUnique Make hash value unique for each direction - * @return The hash value calculated for this packet or 0 if the packet doesn't contain 5-tuple - */ - uint32_t hash5Tuple(Packet* packet, bool const& directionUnique = false); - - /** - * A method that is given a packet and calculates a hash value by the packet's 2-tuple (IP src + IP dst). Supports - * IPv4 and IPv6. For packets which aren't IPv4/6 the value of 0 will be returned - * @param[in] packet The packet to calculate hash for - * @return The hash value calculated for this packet or 0 if the packet isn't IPv4/6 - */ - uint32_t hash2Tuple(Packet* packet); - -} // namespace pcpp - -#endif /* PACKETPP_PACKET_UTILS */ +#ifndef PACKETPP_PACKET_UTILS +#define PACKETPP_PACKET_UTILS + +#include "Packet.h" + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + /** + * A struct that represent a single buffer + */ + template + struct ScalarBuffer + { + /** + * The pointer to the buffer + */ + T* buffer; + + /** + * Buffer length + */ + size_t len; + }; + + /** + * Computes the checksum for a vector of buffers + * @param[in] vec The vector of buffers + * @param[in] vecSize Number of ScalarBuffers in vector + * @return The checksum result + */ + uint16_t computeChecksum(ScalarBuffer vec[], size_t vecSize); + + /** + * Computes Fowler-Noll-Vo (FNV-1) 32bit hash function on an array of byte buffers. The hash is calculated on each + * byte in each byte buffer, as if all byte buffers were one long byte buffer + * @param[in] vec An array of byte buffers (ScalarBuffer of type uint8_t) + * @param[in] vecSize The length of vec + * @return The 32bit hash value + */ + uint32_t fnvHash(ScalarBuffer vec[], size_t vecSize); + + /** + * Computes Fowler-Noll-Vo (FNV-1) 32bit hash function on a byte buffer + * @param[in] buffer The byte buffer + * @param[in] bufSize The size of the byte buffer + * @return The 32bit hash value + */ + uint32_t fnvHash(uint8_t* buffer, size_t bufSize); + + /** + * A method that is given a packet and calculates a hash value by the packet's 5-tuple. Supports IPv4, IPv6, + * TCP and UDP. For packets which doesn't have 5-tuple (for example: packets which aren't IPv4/6 or aren't + * TCP/UDP) the value of 0 will be returned + * @param[in] packet The packet to calculate hash for + * @param[in] directionUnique Make hash value unique for each direction + * @return The hash value calculated for this packet or 0 if the packet doesn't contain 5-tuple + */ + uint32_t hash5Tuple(Packet* packet, bool const& directionUnique = false); + + /** + * A method that is given a packet and calculates a hash value by the packet's 2-tuple (IP src + IP dst). Supports + * IPv4 and IPv6. For packets which aren't IPv4/6 the value of 0 will be returned + * @param[in] packet The packet to calculate hash for + * @return The hash value calculated for this packet or 0 if the packet isn't IPv4/6 + */ + uint32_t hash2Tuple(Packet* packet); + +} // namespace pcpp + +#endif /* PACKETPP_PACKET_UTILS */ diff --git a/Packet++/header/ProtocolType.h b/Packet++/header/ProtocolType.h index d7ad6a8ac3..0a98ec518f 100644 --- a/Packet++/header/ProtocolType.h +++ b/Packet++/header/ProtocolType.h @@ -1,320 +1,320 @@ -#ifndef PCAPPP_PROTOCOL_TYPES -#define PCAPPP_PROTOCOL_TYPES - -#include - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - /** - * @typedef ProtocolType - * Representing all protocols supported by PcapPlusPlus - */ - typedef uint64_t ProtocolType; - - /** - * Unknown protocol (or unsupported by PcapPlusPlus) - */ - const ProtocolType UnknownProtocol = 0x00; - - /** - * Ethernet protocol - */ - const ProtocolType Ethernet = 0x01; - - /** - * IPv4 protocol - */ - const ProtocolType IPv4 = 0x02; - - /** - * IPv6 protocol - */ - const ProtocolType IPv6 = 0x04; - - /** - * IP protocol (aggregation bitmask of IPv4 and IPv6 protocols) - */ - const ProtocolType IP = 0x06; - - /** - * TCP protocol - */ - const ProtocolType TCP = 0x08; - - /** - * UDP protocol - */ - const ProtocolType UDP = 0x10; - - /** - * HTTP request protocol - */ - const ProtocolType HTTPRequest = 0x20; - - /** - * HTTP response protocol - */ - const ProtocolType HTTPResponse = 0x40; - - /** - * HTTP protocol (aggregation bitmask of HTTP request and HTTP response protocols) - */ - const ProtocolType HTTP = 0x60; - - /** - * ARP protocol - */ - const ProtocolType ARP = 0x80; - - /** - * VLAN protocol - */ - const ProtocolType VLAN = 0x100; - - /** - * ICMP protocol - */ - const ProtocolType ICMP = 0x200; - - /** - * PPPoE session protocol - */ - const ProtocolType PPPoESession = 0x400; - - /** - * PPPoE discovery protocol - */ - const ProtocolType PPPoEDiscovery = 0x800; - - /** - * PPPoE protocol (aggregation bitmask of PPPoESession and PPPoEDiscovery protocols) - */ - const ProtocolType PPPoE = 0xc00; - - /** - * DNS protocol - */ - const ProtocolType DNS = 0x1000; - - /** - * MPLS protocol - */ - const ProtocolType MPLS = 0x2000; - - /** - * GRE version 0 protocol - */ - const ProtocolType GREv0 = 0x4000; - - /** - * GRE version 1 protocol - */ - const ProtocolType GREv1 = 0x8000; - - /** - * GRE protocol (aggregation bitmask of GREv0 and GREv1 protocols) - */ - const ProtocolType GRE = 0xc000; - - /** - * PPP for PPTP protocol - */ - const ProtocolType PPP_PPTP = 0x10000; - - /** - * SSL/TLS protocol - */ - const ProtocolType SSL = 0x20000; - - /** - * SLL (Linux cooked capture) protocol - */ - const ProtocolType SLL = 0x40000; - - /** - * DHCP/BOOTP protocol - */ - const ProtocolType DHCP = 0x80000; - - /** - * Null/Loopback protocol - */ - const ProtocolType NULL_LOOPBACK = 0x100000; - - /** - * IGMP protocol - */ - const ProtocolType IGMP = 0xE00000; - - /** - * IGMPv1 protocol - */ - const ProtocolType IGMPv1 = 0x200000; - - /** - * IGMPv2 protocol - */ - const ProtocolType IGMPv2 = 0x400000; - - /** - * IGMPv3 protocol - */ - const ProtocolType IGMPv3 = 0x800000; - - /** - * Generic payload (no specific protocol) - */ - const ProtocolType GenericPayload = 0x1000000; - - /** - * VXLAN protocol - */ - const ProtocolType VXLAN = 0x2000000; - - /** - * SIP request protocol - */ - const ProtocolType SIPRequest = 0x4000000; - - /** - * SIP response protocol - */ - const ProtocolType SIPResponse = 0x8000000; - - /** - * SIP protocol (aggregation bitmask of SIPRequest and SIPResponse protocols) - */ - const ProtocolType SIP = 0xc000000; - - /** - * SDP protocol - */ - const ProtocolType SDP = 0x10000000; - - /** - * Packet trailer - */ - const ProtocolType PacketTrailer = 0x20000000; - - /** - * RADIUS protocol - */ - const ProtocolType Radius = 0x40000000; - - /** - * GTPv1 protocol - */ - const ProtocolType GTPv1 = 0x80000000; - - /** - * GTP protocol (currently the same as GTPv1) - */ - const ProtocolType GTP = 0x80000000; - - /** - * IEEE 802.3 Ethernet protocol - */ - const ProtocolType EthernetDot3 = 0x100000000; - - /** - * Border Gateway Protocol (BGP) version 4 protocol - */ - const ProtocolType BGP = 0x200000000; - - /** - * SSH version 2 protocol - */ - const ProtocolType SSH = 0x400000000; - - /** - * IPSec Authentication Header (AH) protocol - */ - const ProtocolType AuthenticationHeader = 0x800000000; - - /** - * IPSec Encapsulating Security Payload (ESP) protocol - */ - const ProtocolType ESP = 0x1000000000; - - /** - * IPSec protocol (aggregation bitmask of AH and ESP protocols) - */ - const ProtocolType IPSec = 0x1800000000; - - /** - * Dynamic Host Configuration Protocol version 6 (DHCPv6) protocol - */ - const ProtocolType DHCPv6 = 0x2000000000; - - /** - * Network Time (NTP) Protocol - */ - const ProtocolType NTP = 0x4000000000; - - /** - * Telnet Protocol - */ - const ProtocolType Telnet = 0x8000000000; - - /** - * File Transfer (FTP) Protocol - */ - const ProtocolType FTP = 0x10000000000; - - /** - * ICMPv6 protocol - */ - const ProtocolType ICMPv6 = 0x20000000000; - - /** - * Spanning Tree Protocol - */ - const ProtocolType STP = 0x40000000000; - - /** - * Logical Link Control (LLC) - */ - const ProtocolType LLC = 0x80000000000; - - /** - * SOME/IP Base protocol - */ - const ProtocolType SomeIP = 0x100000000000; - - /** - * Wake On LAN (WOL) Protocol - */ - const ProtocolType WakeOnLan = 0x200000000000; - - /** - * An enum representing OSI model layers - */ - enum OsiModelLayer - { - /** Physical layer (layer 1) */ - OsiModelPhysicalLayer = 1, - /** Data link layer (layer 2) */ - OsiModelDataLinkLayer = 2, - /** Network layer (layer 3) */ - OsiModelNetworkLayer = 3, - /** Transport layer (layer 4) */ - OsiModelTransportLayer = 4, - /** Session layer (layer 5) */ - OsiModelSesionLayer = 5, - /** Presentation layer (layer 6) */ - OsiModelPresentationLayer = 6, - /** Application layer (layer 7) */ - OsiModelApplicationLayer = 7, - /** Unknown / null layer */ - OsiModelLayerUnknown = 8 - }; - -} //namespace pcpp - -#endif +#ifndef PCAPPP_PROTOCOL_TYPES +#define PCAPPP_PROTOCOL_TYPES + +#include + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + /** + * @typedef ProtocolType + * Representing all protocols supported by PcapPlusPlus + */ + typedef uint64_t ProtocolType; + + /** + * Unknown protocol (or unsupported by PcapPlusPlus) + */ + const ProtocolType UnknownProtocol = 0x00; + + /** + * Ethernet protocol + */ + const ProtocolType Ethernet = 0x01; + + /** + * IPv4 protocol + */ + const ProtocolType IPv4 = 0x02; + + /** + * IPv6 protocol + */ + const ProtocolType IPv6 = 0x04; + + /** + * IP protocol (aggregation bitmask of IPv4 and IPv6 protocols) + */ + const ProtocolType IP = 0x06; + + /** + * TCP protocol + */ + const ProtocolType TCP = 0x08; + + /** + * UDP protocol + */ + const ProtocolType UDP = 0x10; + + /** + * HTTP request protocol + */ + const ProtocolType HTTPRequest = 0x20; + + /** + * HTTP response protocol + */ + const ProtocolType HTTPResponse = 0x40; + + /** + * HTTP protocol (aggregation bitmask of HTTP request and HTTP response protocols) + */ + const ProtocolType HTTP = 0x60; + + /** + * ARP protocol + */ + const ProtocolType ARP = 0x80; + + /** + * VLAN protocol + */ + const ProtocolType VLAN = 0x100; + + /** + * ICMP protocol + */ + const ProtocolType ICMP = 0x200; + + /** + * PPPoE session protocol + */ + const ProtocolType PPPoESession = 0x400; + + /** + * PPPoE discovery protocol + */ + const ProtocolType PPPoEDiscovery = 0x800; + + /** + * PPPoE protocol (aggregation bitmask of PPPoESession and PPPoEDiscovery protocols) + */ + const ProtocolType PPPoE = 0xc00; + + /** + * DNS protocol + */ + const ProtocolType DNS = 0x1000; + + /** + * MPLS protocol + */ + const ProtocolType MPLS = 0x2000; + + /** + * GRE version 0 protocol + */ + const ProtocolType GREv0 = 0x4000; + + /** + * GRE version 1 protocol + */ + const ProtocolType GREv1 = 0x8000; + + /** + * GRE protocol (aggregation bitmask of GREv0 and GREv1 protocols) + */ + const ProtocolType GRE = 0xc000; + + /** + * PPP for PPTP protocol + */ + const ProtocolType PPP_PPTP = 0x10000; + + /** + * SSL/TLS protocol + */ + const ProtocolType SSL = 0x20000; + + /** + * SLL (Linux cooked capture) protocol + */ + const ProtocolType SLL = 0x40000; + + /** + * DHCP/BOOTP protocol + */ + const ProtocolType DHCP = 0x80000; + + /** + * Null/Loopback protocol + */ + const ProtocolType NULL_LOOPBACK = 0x100000; + + /** + * IGMP protocol + */ + const ProtocolType IGMP = 0xE00000; + + /** + * IGMPv1 protocol + */ + const ProtocolType IGMPv1 = 0x200000; + + /** + * IGMPv2 protocol + */ + const ProtocolType IGMPv2 = 0x400000; + + /** + * IGMPv3 protocol + */ + const ProtocolType IGMPv3 = 0x800000; + + /** + * Generic payload (no specific protocol) + */ + const ProtocolType GenericPayload = 0x1000000; + + /** + * VXLAN protocol + */ + const ProtocolType VXLAN = 0x2000000; + + /** + * SIP request protocol + */ + const ProtocolType SIPRequest = 0x4000000; + + /** + * SIP response protocol + */ + const ProtocolType SIPResponse = 0x8000000; + + /** + * SIP protocol (aggregation bitmask of SIPRequest and SIPResponse protocols) + */ + const ProtocolType SIP = 0xc000000; + + /** + * SDP protocol + */ + const ProtocolType SDP = 0x10000000; + + /** + * Packet trailer + */ + const ProtocolType PacketTrailer = 0x20000000; + + /** + * RADIUS protocol + */ + const ProtocolType Radius = 0x40000000; + + /** + * GTPv1 protocol + */ + const ProtocolType GTPv1 = 0x80000000; + + /** + * GTP protocol (currently the same as GTPv1) + */ + const ProtocolType GTP = 0x80000000; + + /** + * IEEE 802.3 Ethernet protocol + */ + const ProtocolType EthernetDot3 = 0x100000000; + + /** + * Border Gateway Protocol (BGP) version 4 protocol + */ + const ProtocolType BGP = 0x200000000; + + /** + * SSH version 2 protocol + */ + const ProtocolType SSH = 0x400000000; + + /** + * IPSec Authentication Header (AH) protocol + */ + const ProtocolType AuthenticationHeader = 0x800000000; + + /** + * IPSec Encapsulating Security Payload (ESP) protocol + */ + const ProtocolType ESP = 0x1000000000; + + /** + * IPSec protocol (aggregation bitmask of AH and ESP protocols) + */ + const ProtocolType IPSec = 0x1800000000; + + /** + * Dynamic Host Configuration Protocol version 6 (DHCPv6) protocol + */ + const ProtocolType DHCPv6 = 0x2000000000; + + /** + * Network Time (NTP) Protocol + */ + const ProtocolType NTP = 0x4000000000; + + /** + * Telnet Protocol + */ + const ProtocolType Telnet = 0x8000000000; + + /** + * File Transfer (FTP) Protocol + */ + const ProtocolType FTP = 0x10000000000; + + /** + * ICMPv6 protocol + */ + const ProtocolType ICMPv6 = 0x20000000000; + + /** + * Spanning Tree Protocol + */ + const ProtocolType STP = 0x40000000000; + + /** + * Logical Link Control (LLC) + */ + const ProtocolType LLC = 0x80000000000; + + /** + * SOME/IP Base protocol + */ + const ProtocolType SomeIP = 0x100000000000; + + /** + * Wake On LAN (WOL) Protocol + */ + const ProtocolType WakeOnLan = 0x200000000000; + + /** + * An enum representing OSI model layers + */ + enum OsiModelLayer + { + /** Physical layer (layer 1) */ + OsiModelPhysicalLayer = 1, + /** Data link layer (layer 2) */ + OsiModelDataLinkLayer = 2, + /** Network layer (layer 3) */ + OsiModelNetworkLayer = 3, + /** Transport layer (layer 4) */ + OsiModelTransportLayer = 4, + /** Session layer (layer 5) */ + OsiModelSesionLayer = 5, + /** Presentation layer (layer 6) */ + OsiModelPresentationLayer = 6, + /** Application layer (layer 7) */ + OsiModelApplicationLayer = 7, + /** Unknown / null layer */ + OsiModelLayerUnknown = 8 + }; + +} //namespace pcpp + +#endif diff --git a/Packet++/header/RawPacket.h b/Packet++/header/RawPacket.h index b775f5778c..50ee80d226 100644 --- a/Packet++/header/RawPacket.h +++ b/Packet++/header/RawPacket.h @@ -1,455 +1,455 @@ -#ifndef PCAPPP_RAW_PACKET -#define PCAPPP_RAW_PACKET - -#include -#ifdef _MSC_VER -#include -#include -#else -#include -#endif -#include - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - - /** - * An enum describing all known link layer type. Taken from: http://www.tcpdump.org/linktypes.html . - */ - enum LinkLayerType - { - /** BSD loopback encapsulation */ - LINKTYPE_NULL = 0, - /** IEEE 802.3 Ethernet */ - LINKTYPE_ETHERNET = 1, - /** AX.25 packet */ - LINKTYPE_AX25 = 3, - /** IEEE 802.5 Token Ring */ - LINKTYPE_IEEE802_5 = 6, - /** ARCNET Data Packets */ - LINKTYPE_ARCNET_BSD = 7, - /** SLIP, encapsulated with a LINKTYPE_SLIP header */ - LINKTYPE_SLIP = 8, - /** PPP, as per RFC 1661 and RFC 1662 */ - LINKTYPE_PPP = 9, - /** FDDI, as specified by ANSI INCITS 239-1994 */ - LINKTYPE_FDDI = 10, - /** Raw IP */ - LINKTYPE_DLT_RAW1 = 12, - /** Raw IP (OpenBSD) */ - LINKTYPE_DLT_RAW2 = 14, - /** PPP in HDLC-like framing, as per RFC 1662, or Cisco PPP with HDLC framing, as per section 4.3.1 of RFC 1547 */ - LINKTYPE_PPP_HDLC = 50, - /** PPPoE */ - LINKTYPE_PPP_ETHER = 51, - /** RFC 1483 LLC/SNAP-encapsulated ATM */ - LINKTYPE_ATM_RFC1483 = 100, - /** Raw IP */ - LINKTYPE_RAW = 101, - /** Cisco PPP with HDLC framing */ - LINKTYPE_C_HDLC = 104, - /** IEEE 802.11 wireless LAN */ - LINKTYPE_IEEE802_11 = 105, - /** Frame Relay */ - LINKTYPE_FRELAY = 107, - /** OpenBSD loopback encapsulation */ - LINKTYPE_LOOP = 108, - /** Linux "cooked" capture encapsulation */ - LINKTYPE_LINUX_SLL = 113, - /** Apple LocalTalk */ - LINKTYPE_LTALK = 114, - /** OpenBSD pflog */ - LINKTYPE_PFLOG = 117, - /** Prism monitor mode information followed by an 802.11 header */ - LINKTYPE_IEEE802_11_PRISM = 119, - /** RFC 2625 IP-over-Fibre Channel */ - LINKTYPE_IP_OVER_FC = 122, - /** ATM traffic, encapsulated as per the scheme used by SunATM devices */ - LINKTYPE_SUNATM = 123, - /** Radiotap link-layer information followed by an 802.11 header */ - LINKTYPE_IEEE802_11_RADIOTAP = 127, - /** ARCNET Data Packets, as described by the ARCNET Trade Association standard ATA 878.1-1999 */ - LINKTYPE_ARCNET_LINUX = 129, - /** Apple IP-over-IEEE 1394 cooked header */ - LINKTYPE_APPLE_IP_OVER_IEEE1394 = 138, - /** Signaling System 7 Message Transfer Part Level 2 */ - LINKTYPE_MTP2_WITH_PHDR = 139, - /** Signaling System 7 Message Transfer Part Level 2 */ - LINKTYPE_MTP2 = 140, - /** Signaling System 7 Message Transfer Part Level 3 */ - LINKTYPE_MTP3 = 141, - /** Signaling System 7 Signalling Connection Control Part */ - LINKTYPE_SCCP = 142, - /** Signaling System 7 Signalling Connection Control Part */ - LINKTYPE_DOCSIS = 143, - /** Linux-IrDA packets */ - LINKTYPE_LINUX_IRDA = 144, - /** Reserved for private use */ - LINKTYPE_USER0 = 147, - /** Reserved for private use */ - LINKTYPE_USER1 = 148, - /** Reserved for private use */ - LINKTYPE_USER2 = 149, - /** Reserved for private use */ - LINKTYPE_USER3 = 150, - /** Reserved for private use */ - LINKTYPE_USER4 = 151, - /** Reserved for private use */ - LINKTYPE_USER5 = 152, - /** Reserved for private use */ - LINKTYPE_USER6 = 153, - /** Reserved for private use */ - LINKTYPE_USER7 = 154, - /** Reserved for private use */ - LINKTYPE_USER8 = 155, - /** Reserved for private use */ - LINKTYPE_USER9 = 156, - /** Reserved for private use */ - LINKTYPE_USER10 = 157, - /** Reserved for private use */ - LINKTYPE_USER11 = 158, - /** Reserved for private use */ - LINKTYPE_USER12 = 159, - /** Reserved for private use */ - LINKTYPE_USER13 = 160, - /** Reserved for private use */ - LINKTYPE_USER14 = 161, - /** Reserved for private use */ - LINKTYPE_USER15 = 162, - /** AVS monitor mode information followed by an 802.11 header */ - LINKTYPE_IEEE802_11_AVS = 163, - /** BACnet MS/TP frames */ - LINKTYPE_BACNET_MS_TP = 165, - /** PPP in HDLC-like encapsulation, like LINKTYPE_PPP_HDLC, but with the 0xff address byte replaced by a direction indication - 0x00 for incoming and 0x01 for outgoing */ - LINKTYPE_PPP_PPPD = 166, - /** General Packet Radio Service Logical Link Control */ - LINKTYPE_GPRS_LLC = 169, - /** Transparent-mapped generic framing procedure */ - LINKTYPE_GPF_T = 170, - /** Frame-mapped generic framing procedure */ - LINKTYPE_GPF_F = 171, - /** Link Access Procedures on the D Channel (LAPD) frames */ - LINKTYPE_LINUX_LAPD = 177, - /** Bluetooth HCI UART transport layer */ - LINKTYPE_BLUETOOTH_HCI_H4 = 187, - /** USB packets, beginning with a Linux USB header */ - LINKTYPE_USB_LINUX = 189, - /** Per-Packet Information information */ - LINKTYPE_PPI = 192, - /** IEEE 802.15.4 wireless Personal Area Network */ - LINKTYPE_IEEE802_15_4 = 195, - /** Various link-layer types, with a pseudo-header, for SITA */ - LINKTYPE_SITA = 196, - /** Various link-layer types, with a pseudo-header, for Endace DAG cards; encapsulates Endace ERF record */ - LINKTYPE_ERF = 197, - /** Bluetooth HCI UART transport layer */ - LINKTYPE_BLUETOOTH_HCI_H4_WITH_PHDR = 201, - /** AX.25 packet, with a 1-byte KISS header containing a type indicator */ - LINKTYPE_AX25_KISS = 202, - /** Link Access Procedures on the D Channel (LAPD) frames */ - LINKTYPE_LAPD = 203, - /** PPP, as per RFC 1661 and RFC 1662, preceded with a one-byte pseudo-header with a zero value meaning "received by this host" and a non-zero value meaning "sent by this host" */ - LINKTYPE_PPP_WITH_DIR = 204, - /** Cisco PPP with HDLC framing */ - LINKTYPE_C_HDLC_WITH_DIR = 205, - /** Frame Relay */ - LINKTYPE_FRELAY_WITH_DIR = 206, - /** IPMB over an I2C circuit */ - LINKTYPE_IPMB_LINUX = 209, - /** IEEE 802.15.4 wireless Personal Area Network */ - LINKTYPE_IEEE802_15_4_NONASK_PHY = 215, - /** USB packets, beginning with a Linux USB header */ - LINKTYPE_USB_LINUX_MMAPPED = 220, - /** Fibre Channel FC-2 frames, beginning with a Frame_Header */ - LINKTYPE_FC_2 = 224, - /** Fibre Channel FC-2 frames */ - LINKTYPE_FC_2_WITH_FRAME_DELIMS = 225, - /** Solaris ipnet pseudo-header */ - LINKTYPE_IPNET = 226, - /** CAN (Controller Area Network) frames, with a pseudo-header as supplied by Linux SocketCAN */ - LINKTYPE_CAN_SOCKETCAN = 227, - /** Raw IPv4; the packet begins with an IPv4 header */ - LINKTYPE_IPV4 = 228, - /** Raw IPv6; the packet begins with an IPv6 header */ - LINKTYPE_IPV6 = 229, - /** IEEE 802.15.4 wireless Personal Area Network, without the FCS at the end of the frame */ - LINKTYPE_IEEE802_15_4_NOFCS = 230, - /** Raw D-Bus messages, starting with the endianness flag, followed by the message type, etc., but without the authentication handshake before the message sequence */ - LINKTYPE_DBUS = 231, - /** DVB-CI (DVB Common Interface for communication between a PC Card module and a DVB receiver), with the message format specified by the PCAP format for DVB-CI specification */ - LINKTYPE_DVB_CI = 235, - /** Variant of 3GPP TS 27.010 multiplexing protocol (similar to, but not the same as, 27.010) */ - LINKTYPE_MUX27010 = 236, - /** D_PDUs as described by NATO standard STANAG 5066, starting with the synchronization sequence, and including both header and data CRCs */ - LINKTYPE_STANAG_5066_D_PDU = 237, - /** Linux netlink NETLINK NFLOG socket log messages */ - LINKTYPE_NFLOG = 239, - /** Pseudo-header for Hilscher Gesellschaft für Systemautomation mbH netANALYZER devices, followed by an Ethernet frame, beginning with the MAC header and ending with the FCS */ - LINKTYPE_NETANALYZER = 240, - /** Pseudo-header for Hilscher Gesellschaft für Systemautomation mbH netANALYZER devices, followed by an Ethernet frame, beginning with the preamble, SFD, and MAC header, and ending with the FCS */ - LINKTYPE_NETANALYZER_TRANSPARENT = 241, - /** IP-over-InfiniBand, as specified by RFC 4391 section 6 */ - LINKTYPE_IPOIB = 242, - /** MPEG-2 Transport Stream transport packets, as specified by ISO 13818-1/ITU-T Recommendation H.222.0 */ - LINKTYPE_MPEG_2_TS = 243, - /** Pseudo-header for ng4T GmbH's UMTS Iub/Iur-over-ATM and Iub/Iur-over-IP format as used by their ng40 protocol tester */ - LINKTYPE_NG40 = 244, - /** Pseudo-header for NFC LLCP packet captures, followed by frame data for the LLCP Protocol as specified by NFCForum-TS-LLCP_1.1 */ - LINKTYPE_NFC_LLCP = 245, - /** Raw InfiniBand frames, starting with the Local Routing Header */ - LINKTYPE_INFINIBAND = 247, - /** SCTP packets, as defined by RFC 4960, with no lower-level protocols such as IPv4 or IPv6 */ - LINKTYPE_SCTP = 248, - /** USB packets, beginning with a USBPcap header */ - LINKTYPE_USBPCAP = 249, - /** Serial-line packet header for the Schweitzer Engineering Laboratories "RTAC" product */ - LINKTYPE_RTAC_SERIAL = 250, - /** Bluetooth Low Energy air interface Link Layer packets */ - LINKTYPE_BLUETOOTH_LE_LL = 251, - /** Linux Netlink capture encapsulation */ - LINKTYPE_NETLINK = 253, - /** Bluetooth Linux Monitor encapsulation of traffic for the BlueZ stack */ - LINKTYPE_BLUETOOTH_LINUX_MONITOR = 254, - /** Bluetooth Basic Rate and Enhanced Data Rate baseband packets */ - LINKTYPE_BLUETOOTH_BREDR_BB = 255, - /** Bluetooth Low Energy link-layer packets */ - LINKTYPE_BLUETOOTH_LE_LL_WITH_PHDR = 256, - /** PROFIBUS data link layer packets, as specified by IEC standard 61158-6-3 */ - LINKTYPE_PROFIBUS_DL = 257, - /** Apple PKTAP capture encapsulation */ - LINKTYPE_PKTAP = 258, - /** Ethernet-over-passive-optical-network packets */ - LINKTYPE_EPON = 259, - /** IPMI trace packets, as specified by Table 3-20 "Trace Data Block Format" in the PICMG HPM.2 specification */ - LINKTYPE_IPMI_HPM_2 = 260, - /** Per Joshua Wright , formats for Z-Wave RF profiles R1 and R2 captures */ - LINKTYPE_ZWAVE_R1_R2 = 261, - /** Per Joshua Wright , formats for Z-Wave RF profile R3 captures */ - LINKTYPE_ZWAVE_R3 = 262, - /** Formats for WattStopper Digital Lighting Management (DLM) and Legrand Nitoo Open protocol common packet structure captures */ - LINKTYPE_WATTSTOPPER_DLM = 263, - /** Messages between ISO 14443 contactless smartcards (Proximity Integrated Circuit Card, PICC) and card readers (Proximity Coupling Device, PCD), with the message format specified by the PCAP format for ISO14443 specification */ - LINKTYPE_ISO_14443 = 264 - }; - - /** - * Max packet size supported - */ -#define PCPP_MAX_PACKET_SIZE 65536 - - /** - * @class RawPacket - * This class holds the packet as raw (not parsed) data. The data is held as byte array. In addition to the data itself - * every instance also holds a timestamp representing the time the packet was received by the NIC. - * RawPacket instance isn't read only. The user can change the packet data, add or remove data, etc. - */ - class RawPacket - { - protected: - uint8_t* m_RawData; - int m_RawDataLen; - int m_FrameLength; - timespec m_TimeStamp; - bool m_DeleteRawDataAtDestructor; - bool m_RawPacketSet; - LinkLayerType m_LinkLayerType; - void init(bool deleteRawDataAtDestructor = true); - void copyDataFrom(const RawPacket& other, bool allocateData = true); - public: - /** - * A constructor that receives a pointer to the raw data (allocated elsewhere). This constructor is usually used when packet - * is captured using a packet capturing engine (like libPcap. WinPcap, Npcap, PF_RING, etc.). The capturing engine allocates the raw data - * memory and give the user a pointer to it + a timestamp it has arrived to the device - * @param[in] pRawData A pointer to the raw data - * @param[in] rawDataLen The raw data length in bytes - * @param[in] timestamp The timestamp packet was received by the NIC (in usec precision) - * @param[in] deleteRawDataAtDestructor An indicator whether raw data pointer should be freed when the instance is freed or not. If set - * to 'true' than pRawData will be freed when instanced is being freed - * @param[in] layerType The link layer type of this raw packet. The default is Ethernet - */ - RawPacket(const uint8_t* pRawData, int rawDataLen, timeval timestamp, bool deleteRawDataAtDestructor, LinkLayerType layerType = LINKTYPE_ETHERNET); - - /** - * A constructor that receives a pointer to the raw data (allocated elsewhere). This constructor is usually used when packet - * is captured using a packet capturing engine (like libPcap. WinPcap, Npcap, PF_RING, etc.). The capturing engine allocates the raw data - * memory and give the user a pointer to it + a timestamp it has arrived to the device - * @param[in] pRawData A pointer to the raw data - * @param[in] rawDataLen The raw data length in bytes - * @param[in] timestamp The timestamp packet was received by the NIC (in nsec precision) - * @param[in] deleteRawDataAtDestructor An indicator whether raw data pointer should be freed when the instance is freed or not. If set - * to 'true' than pRawData will be freed when instanced is being freed - * @param[in] layerType The link layer type of this raw packet. The default is Ethernet - */ - RawPacket(const uint8_t* pRawData, int rawDataLen, timespec timestamp, bool deleteRawDataAtDestructor, LinkLayerType layerType = LINKTYPE_ETHERNET); - - /** - * A default constructor that initializes class'es attributes to default value: - * - data pointer is set to NULL - * - data length is set to 0 - * - deleteRawDataAtDestructor is set to 'true' - * @todo timestamp isn't set here to a default value - */ - RawPacket(); - - /** - * A destructor for this class. Frees the raw data if deleteRawDataAtDestructor was set to 'true' - */ - virtual ~RawPacket(); - - /** - * A copy constructor that copies all data from another instance. Notice all raw data is copied (using memcpy), so when the original or - * the other instance are freed, the other won't be affected - * @param[in] other The instance to copy from - */ - RawPacket(const RawPacket& other); - - /** - * Assignment operator overload for this class. When using this operator on an already initialized RawPacket instance, - * the original raw data is freed first. Then the other instance is copied to this instance, the same way the copy constructor works - * @todo free raw data only if deleteRawDataAtDestructor was set to 'true' - * @param[in] other The instance to copy from - */ - RawPacket& operator=(const RawPacket& other); - - /** - * @return RawPacket object type. Each derived class should return a different value - */ - virtual uint8_t getObjectType() const { return 0; } - - /** - * Set a raw data. If data was already set and deleteRawDataAtDestructor was set to 'true' the old data will be freed first - * @param[in] pRawData A pointer to the new raw data - * @param[in] rawDataLen The new raw data length in bytes - * @param[in] timestamp The timestamp packet was received by the NIC (in usec precision) - * @param[in] layerType The link layer type for this raw data - * @param[in] frameLength When reading from pcap files, sometimes the captured length is different from the actual packet length. This parameter represents the packet - * length. This parameter is optional, if not set or set to -1 it is assumed both lengths are equal - * @return True if raw data was set successfully, false otherwise - */ - virtual bool setRawData(const uint8_t* pRawData, int rawDataLen, timeval timestamp, LinkLayerType layerType = LINKTYPE_ETHERNET, int frameLength = -1); - - /** - * Set a raw data. If data was already set and deleteRawDataAtDestructor was set to 'true' the old data will be freed first - * @param[in] pRawData A pointer to the new raw data - * @param[in] rawDataLen The new raw data length in bytes - * @param[in] timestamp The timestamp packet was received by the NIC (in nsec precision) - * @param[in] layerType The link layer type for this raw data - * @param[in] frameLength When reading from pcap files, sometimes the captured length is different from the actual packet length. This parameter represents the packet - * length. This parameter is optional, if not set or set to -1 it is assumed both lengths are equal - * @return True if raw data was set successfully, false otherwise - */ - virtual bool setRawData(const uint8_t* pRawData, int rawDataLen, timespec timestamp, LinkLayerType layerType = LINKTYPE_ETHERNET, int frameLength = -1); - - /** - * Get raw data pointer - * @return A read-only pointer to the raw data - */ - const uint8_t* getRawData() const { return m_RawData; } - - /** - * Get the link layer type - * @return the type of the link layer - */ - LinkLayerType getLinkLayerType() const { return m_LinkLayerType; } - - /** - * This static method validates whether a link type integer value is valid - * @param[in] linkTypeValue Link type integer value - * @return True if the link type value is valid and can be casted into LinkLayerType enum, false otherwise - */ - static bool isLinkTypeValid(int linkTypeValue); - - /** - * Get raw data length in bytes - * @return Raw data length in bytes - */ - int getRawDataLen() const { return m_RawDataLen; } - - /** - * Get frame length in bytes - * @return frame length in bytes - */ - int getFrameLength() const { return m_FrameLength; } - /** - * Get raw data timestamp - * @return Raw data timestamp - */ - timespec getPacketTimeStamp() const { return m_TimeStamp; } - - /** - * Set raw packet timestamp with usec precision - * @param[in] timestamp The timestamp to set (with usec precision) - * @return True if timestamp was set successfully, false otherwise - */ - virtual bool setPacketTimeStamp(timeval timestamp); - - /** - * Set raw packet timestamp with nsec precision - * @param[in] timestamp The timestamp to set (with nsec precision) - * @return True if timestamp was set successfully, false otherwise - */ - virtual bool setPacketTimeStamp(timespec timestamp); - - /** - * Get an indication whether raw data was already set for this instance. - * @return True if raw data was set for this instance. Raw data can be set using the non-default constructor, using setRawData(), using - * the copy constructor or using the assignment operator. Returns false otherwise, for example: if the instance was created using the - * default constructor or clear() was called - */ - bool isPacketSet() const { return m_RawPacketSet; } - - /** - * Clears all members of this instance, meaning setting raw data to NULL, raw data length to 0, etc. Currently raw data is always freed, - * even if deleteRawDataAtDestructor was set to 'false' - * @todo deleteRawDataAtDestructor was set to 'true', don't free the raw data - * @todo set timestamp to a default value as well - */ - virtual void clear(); - - /** - * Append data to the end of current data. This method works without allocating more memory, it just uses memcpy() to copy dataToAppend at - * the end of the current data. This means that the method assumes this memory was already allocated by the user. If it isn't the case then - * this method will cause memory corruption - * @param[in] dataToAppend A pointer to the data to append to current raw data - * @param[in] dataToAppendLen Length in bytes of dataToAppend - */ - virtual void appendData(const uint8_t* dataToAppend, size_t dataToAppendLen); - - /** - * Insert new data at some index of the current data and shift the remaining old data to the end. This method works without allocating more memory, - * it just copies dataToAppend at the relevant index and shifts the remaining data to the end. This means that the method assumes this memory was - * already allocated by the user. If it isn't the case then this method will cause memory corruption - * @param[in] atIndex The index to insert the new data to - * @param[in] dataToInsert A pointer to the new data to insert - * @param[in] dataToInsertLen Length in bytes of dataToInsert - */ - virtual void insertData(int atIndex, const uint8_t* dataToInsert, size_t dataToInsertLen); - - /** - * Remove certain number of bytes from current raw data buffer. All data after the removed bytes will be shifted back - * @param[in] atIndex The index to start removing bytes from - * @param[in] numOfBytesToRemove Number of bytes to remove - * @return True if all bytes were removed successfully, or false if atIndex+numOfBytesToRemove is out-of-bounds of the raw data buffer - */ - virtual bool removeData(int atIndex, size_t numOfBytesToRemove); - - /** - * Re-allocate raw packet buffer meaning add size to it without losing the current packet data. This method allocates the required buffer size as instructed - * by the use and then copies the raw data from the current allocated buffer to the new one. This method can become useful if the user wants to insert or - * append data to the raw data, and the previous allocated buffer is too small, so the user wants to allocate a larger buffer and get RawPacket instance to - * point to it - * @param[in] newBufferLength The new buffer length as required by the user. The method is responsible to allocate the memory - * @return True if data was reallocated successfully, false otherwise - */ - virtual bool reallocateData(size_t newBufferLength); - }; - -} // namespace pcpp - -#endif +#ifndef PCAPPP_RAW_PACKET +#define PCAPPP_RAW_PACKET + +#include +#ifdef _MSC_VER +#include +#include +#else +#include +#endif +#include + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + + /** + * An enum describing all known link layer type. Taken from: http://www.tcpdump.org/linktypes.html . + */ + enum LinkLayerType + { + /** BSD loopback encapsulation */ + LINKTYPE_NULL = 0, + /** IEEE 802.3 Ethernet */ + LINKTYPE_ETHERNET = 1, + /** AX.25 packet */ + LINKTYPE_AX25 = 3, + /** IEEE 802.5 Token Ring */ + LINKTYPE_IEEE802_5 = 6, + /** ARCNET Data Packets */ + LINKTYPE_ARCNET_BSD = 7, + /** SLIP, encapsulated with a LINKTYPE_SLIP header */ + LINKTYPE_SLIP = 8, + /** PPP, as per RFC 1661 and RFC 1662 */ + LINKTYPE_PPP = 9, + /** FDDI, as specified by ANSI INCITS 239-1994 */ + LINKTYPE_FDDI = 10, + /** Raw IP */ + LINKTYPE_DLT_RAW1 = 12, + /** Raw IP (OpenBSD) */ + LINKTYPE_DLT_RAW2 = 14, + /** PPP in HDLC-like framing, as per RFC 1662, or Cisco PPP with HDLC framing, as per section 4.3.1 of RFC 1547 */ + LINKTYPE_PPP_HDLC = 50, + /** PPPoE */ + LINKTYPE_PPP_ETHER = 51, + /** RFC 1483 LLC/SNAP-encapsulated ATM */ + LINKTYPE_ATM_RFC1483 = 100, + /** Raw IP */ + LINKTYPE_RAW = 101, + /** Cisco PPP with HDLC framing */ + LINKTYPE_C_HDLC = 104, + /** IEEE 802.11 wireless LAN */ + LINKTYPE_IEEE802_11 = 105, + /** Frame Relay */ + LINKTYPE_FRELAY = 107, + /** OpenBSD loopback encapsulation */ + LINKTYPE_LOOP = 108, + /** Linux "cooked" capture encapsulation */ + LINKTYPE_LINUX_SLL = 113, + /** Apple LocalTalk */ + LINKTYPE_LTALK = 114, + /** OpenBSD pflog */ + LINKTYPE_PFLOG = 117, + /** Prism monitor mode information followed by an 802.11 header */ + LINKTYPE_IEEE802_11_PRISM = 119, + /** RFC 2625 IP-over-Fibre Channel */ + LINKTYPE_IP_OVER_FC = 122, + /** ATM traffic, encapsulated as per the scheme used by SunATM devices */ + LINKTYPE_SUNATM = 123, + /** Radiotap link-layer information followed by an 802.11 header */ + LINKTYPE_IEEE802_11_RADIOTAP = 127, + /** ARCNET Data Packets, as described by the ARCNET Trade Association standard ATA 878.1-1999 */ + LINKTYPE_ARCNET_LINUX = 129, + /** Apple IP-over-IEEE 1394 cooked header */ + LINKTYPE_APPLE_IP_OVER_IEEE1394 = 138, + /** Signaling System 7 Message Transfer Part Level 2 */ + LINKTYPE_MTP2_WITH_PHDR = 139, + /** Signaling System 7 Message Transfer Part Level 2 */ + LINKTYPE_MTP2 = 140, + /** Signaling System 7 Message Transfer Part Level 3 */ + LINKTYPE_MTP3 = 141, + /** Signaling System 7 Signalling Connection Control Part */ + LINKTYPE_SCCP = 142, + /** Signaling System 7 Signalling Connection Control Part */ + LINKTYPE_DOCSIS = 143, + /** Linux-IrDA packets */ + LINKTYPE_LINUX_IRDA = 144, + /** Reserved for private use */ + LINKTYPE_USER0 = 147, + /** Reserved for private use */ + LINKTYPE_USER1 = 148, + /** Reserved for private use */ + LINKTYPE_USER2 = 149, + /** Reserved for private use */ + LINKTYPE_USER3 = 150, + /** Reserved for private use */ + LINKTYPE_USER4 = 151, + /** Reserved for private use */ + LINKTYPE_USER5 = 152, + /** Reserved for private use */ + LINKTYPE_USER6 = 153, + /** Reserved for private use */ + LINKTYPE_USER7 = 154, + /** Reserved for private use */ + LINKTYPE_USER8 = 155, + /** Reserved for private use */ + LINKTYPE_USER9 = 156, + /** Reserved for private use */ + LINKTYPE_USER10 = 157, + /** Reserved for private use */ + LINKTYPE_USER11 = 158, + /** Reserved for private use */ + LINKTYPE_USER12 = 159, + /** Reserved for private use */ + LINKTYPE_USER13 = 160, + /** Reserved for private use */ + LINKTYPE_USER14 = 161, + /** Reserved for private use */ + LINKTYPE_USER15 = 162, + /** AVS monitor mode information followed by an 802.11 header */ + LINKTYPE_IEEE802_11_AVS = 163, + /** BACnet MS/TP frames */ + LINKTYPE_BACNET_MS_TP = 165, + /** PPP in HDLC-like encapsulation, like LINKTYPE_PPP_HDLC, but with the 0xff address byte replaced by a direction indication - 0x00 for incoming and 0x01 for outgoing */ + LINKTYPE_PPP_PPPD = 166, + /** General Packet Radio Service Logical Link Control */ + LINKTYPE_GPRS_LLC = 169, + /** Transparent-mapped generic framing procedure */ + LINKTYPE_GPF_T = 170, + /** Frame-mapped generic framing procedure */ + LINKTYPE_GPF_F = 171, + /** Link Access Procedures on the D Channel (LAPD) frames */ + LINKTYPE_LINUX_LAPD = 177, + /** Bluetooth HCI UART transport layer */ + LINKTYPE_BLUETOOTH_HCI_H4 = 187, + /** USB packets, beginning with a Linux USB header */ + LINKTYPE_USB_LINUX = 189, + /** Per-Packet Information information */ + LINKTYPE_PPI = 192, + /** IEEE 802.15.4 wireless Personal Area Network */ + LINKTYPE_IEEE802_15_4 = 195, + /** Various link-layer types, with a pseudo-header, for SITA */ + LINKTYPE_SITA = 196, + /** Various link-layer types, with a pseudo-header, for Endace DAG cards; encapsulates Endace ERF record */ + LINKTYPE_ERF = 197, + /** Bluetooth HCI UART transport layer */ + LINKTYPE_BLUETOOTH_HCI_H4_WITH_PHDR = 201, + /** AX.25 packet, with a 1-byte KISS header containing a type indicator */ + LINKTYPE_AX25_KISS = 202, + /** Link Access Procedures on the D Channel (LAPD) frames */ + LINKTYPE_LAPD = 203, + /** PPP, as per RFC 1661 and RFC 1662, preceded with a one-byte pseudo-header with a zero value meaning "received by this host" and a non-zero value meaning "sent by this host" */ + LINKTYPE_PPP_WITH_DIR = 204, + /** Cisco PPP with HDLC framing */ + LINKTYPE_C_HDLC_WITH_DIR = 205, + /** Frame Relay */ + LINKTYPE_FRELAY_WITH_DIR = 206, + /** IPMB over an I2C circuit */ + LINKTYPE_IPMB_LINUX = 209, + /** IEEE 802.15.4 wireless Personal Area Network */ + LINKTYPE_IEEE802_15_4_NONASK_PHY = 215, + /** USB packets, beginning with a Linux USB header */ + LINKTYPE_USB_LINUX_MMAPPED = 220, + /** Fibre Channel FC-2 frames, beginning with a Frame_Header */ + LINKTYPE_FC_2 = 224, + /** Fibre Channel FC-2 frames */ + LINKTYPE_FC_2_WITH_FRAME_DELIMS = 225, + /** Solaris ipnet pseudo-header */ + LINKTYPE_IPNET = 226, + /** CAN (Controller Area Network) frames, with a pseudo-header as supplied by Linux SocketCAN */ + LINKTYPE_CAN_SOCKETCAN = 227, + /** Raw IPv4; the packet begins with an IPv4 header */ + LINKTYPE_IPV4 = 228, + /** Raw IPv6; the packet begins with an IPv6 header */ + LINKTYPE_IPV6 = 229, + /** IEEE 802.15.4 wireless Personal Area Network, without the FCS at the end of the frame */ + LINKTYPE_IEEE802_15_4_NOFCS = 230, + /** Raw D-Bus messages, starting with the endianness flag, followed by the message type, etc., but without the authentication handshake before the message sequence */ + LINKTYPE_DBUS = 231, + /** DVB-CI (DVB Common Interface for communication between a PC Card module and a DVB receiver), with the message format specified by the PCAP format for DVB-CI specification */ + LINKTYPE_DVB_CI = 235, + /** Variant of 3GPP TS 27.010 multiplexing protocol (similar to, but not the same as, 27.010) */ + LINKTYPE_MUX27010 = 236, + /** D_PDUs as described by NATO standard STANAG 5066, starting with the synchronization sequence, and including both header and data CRCs */ + LINKTYPE_STANAG_5066_D_PDU = 237, + /** Linux netlink NETLINK NFLOG socket log messages */ + LINKTYPE_NFLOG = 239, + /** Pseudo-header for Hilscher Gesellschaft für Systemautomation mbH netANALYZER devices, followed by an Ethernet frame, beginning with the MAC header and ending with the FCS */ + LINKTYPE_NETANALYZER = 240, + /** Pseudo-header for Hilscher Gesellschaft für Systemautomation mbH netANALYZER devices, followed by an Ethernet frame, beginning with the preamble, SFD, and MAC header, and ending with the FCS */ + LINKTYPE_NETANALYZER_TRANSPARENT = 241, + /** IP-over-InfiniBand, as specified by RFC 4391 section 6 */ + LINKTYPE_IPOIB = 242, + /** MPEG-2 Transport Stream transport packets, as specified by ISO 13818-1/ITU-T Recommendation H.222.0 */ + LINKTYPE_MPEG_2_TS = 243, + /** Pseudo-header for ng4T GmbH's UMTS Iub/Iur-over-ATM and Iub/Iur-over-IP format as used by their ng40 protocol tester */ + LINKTYPE_NG40 = 244, + /** Pseudo-header for NFC LLCP packet captures, followed by frame data for the LLCP Protocol as specified by NFCForum-TS-LLCP_1.1 */ + LINKTYPE_NFC_LLCP = 245, + /** Raw InfiniBand frames, starting with the Local Routing Header */ + LINKTYPE_INFINIBAND = 247, + /** SCTP packets, as defined by RFC 4960, with no lower-level protocols such as IPv4 or IPv6 */ + LINKTYPE_SCTP = 248, + /** USB packets, beginning with a USBPcap header */ + LINKTYPE_USBPCAP = 249, + /** Serial-line packet header for the Schweitzer Engineering Laboratories "RTAC" product */ + LINKTYPE_RTAC_SERIAL = 250, + /** Bluetooth Low Energy air interface Link Layer packets */ + LINKTYPE_BLUETOOTH_LE_LL = 251, + /** Linux Netlink capture encapsulation */ + LINKTYPE_NETLINK = 253, + /** Bluetooth Linux Monitor encapsulation of traffic for the BlueZ stack */ + LINKTYPE_BLUETOOTH_LINUX_MONITOR = 254, + /** Bluetooth Basic Rate and Enhanced Data Rate baseband packets */ + LINKTYPE_BLUETOOTH_BREDR_BB = 255, + /** Bluetooth Low Energy link-layer packets */ + LINKTYPE_BLUETOOTH_LE_LL_WITH_PHDR = 256, + /** PROFIBUS data link layer packets, as specified by IEC standard 61158-6-3 */ + LINKTYPE_PROFIBUS_DL = 257, + /** Apple PKTAP capture encapsulation */ + LINKTYPE_PKTAP = 258, + /** Ethernet-over-passive-optical-network packets */ + LINKTYPE_EPON = 259, + /** IPMI trace packets, as specified by Table 3-20 "Trace Data Block Format" in the PICMG HPM.2 specification */ + LINKTYPE_IPMI_HPM_2 = 260, + /** Per Joshua Wright , formats for Z-Wave RF profiles R1 and R2 captures */ + LINKTYPE_ZWAVE_R1_R2 = 261, + /** Per Joshua Wright , formats for Z-Wave RF profile R3 captures */ + LINKTYPE_ZWAVE_R3 = 262, + /** Formats for WattStopper Digital Lighting Management (DLM) and Legrand Nitoo Open protocol common packet structure captures */ + LINKTYPE_WATTSTOPPER_DLM = 263, + /** Messages between ISO 14443 contactless smartcards (Proximity Integrated Circuit Card, PICC) and card readers (Proximity Coupling Device, PCD), with the message format specified by the PCAP format for ISO14443 specification */ + LINKTYPE_ISO_14443 = 264 + }; + + /** + * Max packet size supported + */ +#define PCPP_MAX_PACKET_SIZE 65536 + + /** + * @class RawPacket + * This class holds the packet as raw (not parsed) data. The data is held as byte array. In addition to the data itself + * every instance also holds a timestamp representing the time the packet was received by the NIC. + * RawPacket instance isn't read only. The user can change the packet data, add or remove data, etc. + */ + class RawPacket + { + protected: + uint8_t* m_RawData; + int m_RawDataLen; + int m_FrameLength; + timespec m_TimeStamp; + bool m_DeleteRawDataAtDestructor; + bool m_RawPacketSet; + LinkLayerType m_LinkLayerType; + void init(bool deleteRawDataAtDestructor = true); + void copyDataFrom(const RawPacket& other, bool allocateData = true); + public: + /** + * A constructor that receives a pointer to the raw data (allocated elsewhere). This constructor is usually used when packet + * is captured using a packet capturing engine (like libPcap. WinPcap, Npcap, PF_RING, etc.). The capturing engine allocates the raw data + * memory and give the user a pointer to it + a timestamp it has arrived to the device + * @param[in] pRawData A pointer to the raw data + * @param[in] rawDataLen The raw data length in bytes + * @param[in] timestamp The timestamp packet was received by the NIC (in usec precision) + * @param[in] deleteRawDataAtDestructor An indicator whether raw data pointer should be freed when the instance is freed or not. If set + * to 'true' than pRawData will be freed when instanced is being freed + * @param[in] layerType The link layer type of this raw packet. The default is Ethernet + */ + RawPacket(const uint8_t* pRawData, int rawDataLen, timeval timestamp, bool deleteRawDataAtDestructor, LinkLayerType layerType = LINKTYPE_ETHERNET); + + /** + * A constructor that receives a pointer to the raw data (allocated elsewhere). This constructor is usually used when packet + * is captured using a packet capturing engine (like libPcap. WinPcap, Npcap, PF_RING, etc.). The capturing engine allocates the raw data + * memory and give the user a pointer to it + a timestamp it has arrived to the device + * @param[in] pRawData A pointer to the raw data + * @param[in] rawDataLen The raw data length in bytes + * @param[in] timestamp The timestamp packet was received by the NIC (in nsec precision) + * @param[in] deleteRawDataAtDestructor An indicator whether raw data pointer should be freed when the instance is freed or not. If set + * to 'true' than pRawData will be freed when instanced is being freed + * @param[in] layerType The link layer type of this raw packet. The default is Ethernet + */ + RawPacket(const uint8_t* pRawData, int rawDataLen, timespec timestamp, bool deleteRawDataAtDestructor, LinkLayerType layerType = LINKTYPE_ETHERNET); + + /** + * A default constructor that initializes class'es attributes to default value: + * - data pointer is set to NULL + * - data length is set to 0 + * - deleteRawDataAtDestructor is set to 'true' + * @todo timestamp isn't set here to a default value + */ + RawPacket(); + + /** + * A destructor for this class. Frees the raw data if deleteRawDataAtDestructor was set to 'true' + */ + virtual ~RawPacket(); + + /** + * A copy constructor that copies all data from another instance. Notice all raw data is copied (using memcpy), so when the original or + * the other instance are freed, the other won't be affected + * @param[in] other The instance to copy from + */ + RawPacket(const RawPacket& other); + + /** + * Assignment operator overload for this class. When using this operator on an already initialized RawPacket instance, + * the original raw data is freed first. Then the other instance is copied to this instance, the same way the copy constructor works + * @todo free raw data only if deleteRawDataAtDestructor was set to 'true' + * @param[in] other The instance to copy from + */ + RawPacket& operator=(const RawPacket& other); + + /** + * @return RawPacket object type. Each derived class should return a different value + */ + virtual uint8_t getObjectType() const { return 0; } + + /** + * Set a raw data. If data was already set and deleteRawDataAtDestructor was set to 'true' the old data will be freed first + * @param[in] pRawData A pointer to the new raw data + * @param[in] rawDataLen The new raw data length in bytes + * @param[in] timestamp The timestamp packet was received by the NIC (in usec precision) + * @param[in] layerType The link layer type for this raw data + * @param[in] frameLength When reading from pcap files, sometimes the captured length is different from the actual packet length. This parameter represents the packet + * length. This parameter is optional, if not set or set to -1 it is assumed both lengths are equal + * @return True if raw data was set successfully, false otherwise + */ + virtual bool setRawData(const uint8_t* pRawData, int rawDataLen, timeval timestamp, LinkLayerType layerType = LINKTYPE_ETHERNET, int frameLength = -1); + + /** + * Set a raw data. If data was already set and deleteRawDataAtDestructor was set to 'true' the old data will be freed first + * @param[in] pRawData A pointer to the new raw data + * @param[in] rawDataLen The new raw data length in bytes + * @param[in] timestamp The timestamp packet was received by the NIC (in nsec precision) + * @param[in] layerType The link layer type for this raw data + * @param[in] frameLength When reading from pcap files, sometimes the captured length is different from the actual packet length. This parameter represents the packet + * length. This parameter is optional, if not set or set to -1 it is assumed both lengths are equal + * @return True if raw data was set successfully, false otherwise + */ + virtual bool setRawData(const uint8_t* pRawData, int rawDataLen, timespec timestamp, LinkLayerType layerType = LINKTYPE_ETHERNET, int frameLength = -1); + + /** + * Get raw data pointer + * @return A read-only pointer to the raw data + */ + const uint8_t* getRawData() const { return m_RawData; } + + /** + * Get the link layer type + * @return the type of the link layer + */ + LinkLayerType getLinkLayerType() const { return m_LinkLayerType; } + + /** + * This static method validates whether a link type integer value is valid + * @param[in] linkTypeValue Link type integer value + * @return True if the link type value is valid and can be casted into LinkLayerType enum, false otherwise + */ + static bool isLinkTypeValid(int linkTypeValue); + + /** + * Get raw data length in bytes + * @return Raw data length in bytes + */ + int getRawDataLen() const { return m_RawDataLen; } + + /** + * Get frame length in bytes + * @return frame length in bytes + */ + int getFrameLength() const { return m_FrameLength; } + /** + * Get raw data timestamp + * @return Raw data timestamp + */ + timespec getPacketTimeStamp() const { return m_TimeStamp; } + + /** + * Set raw packet timestamp with usec precision + * @param[in] timestamp The timestamp to set (with usec precision) + * @return True if timestamp was set successfully, false otherwise + */ + virtual bool setPacketTimeStamp(timeval timestamp); + + /** + * Set raw packet timestamp with nsec precision + * @param[in] timestamp The timestamp to set (with nsec precision) + * @return True if timestamp was set successfully, false otherwise + */ + virtual bool setPacketTimeStamp(timespec timestamp); + + /** + * Get an indication whether raw data was already set for this instance. + * @return True if raw data was set for this instance. Raw data can be set using the non-default constructor, using setRawData(), using + * the copy constructor or using the assignment operator. Returns false otherwise, for example: if the instance was created using the + * default constructor or clear() was called + */ + bool isPacketSet() const { return m_RawPacketSet; } + + /** + * Clears all members of this instance, meaning setting raw data to NULL, raw data length to 0, etc. Currently raw data is always freed, + * even if deleteRawDataAtDestructor was set to 'false' + * @todo deleteRawDataAtDestructor was set to 'true', don't free the raw data + * @todo set timestamp to a default value as well + */ + virtual void clear(); + + /** + * Append data to the end of current data. This method works without allocating more memory, it just uses memcpy() to copy dataToAppend at + * the end of the current data. This means that the method assumes this memory was already allocated by the user. If it isn't the case then + * this method will cause memory corruption + * @param[in] dataToAppend A pointer to the data to append to current raw data + * @param[in] dataToAppendLen Length in bytes of dataToAppend + */ + virtual void appendData(const uint8_t* dataToAppend, size_t dataToAppendLen); + + /** + * Insert new data at some index of the current data and shift the remaining old data to the end. This method works without allocating more memory, + * it just copies dataToAppend at the relevant index and shifts the remaining data to the end. This means that the method assumes this memory was + * already allocated by the user. If it isn't the case then this method will cause memory corruption + * @param[in] atIndex The index to insert the new data to + * @param[in] dataToInsert A pointer to the new data to insert + * @param[in] dataToInsertLen Length in bytes of dataToInsert + */ + virtual void insertData(int atIndex, const uint8_t* dataToInsert, size_t dataToInsertLen); + + /** + * Remove certain number of bytes from current raw data buffer. All data after the removed bytes will be shifted back + * @param[in] atIndex The index to start removing bytes from + * @param[in] numOfBytesToRemove Number of bytes to remove + * @return True if all bytes were removed successfully, or false if atIndex+numOfBytesToRemove is out-of-bounds of the raw data buffer + */ + virtual bool removeData(int atIndex, size_t numOfBytesToRemove); + + /** + * Re-allocate raw packet buffer meaning add size to it without losing the current packet data. This method allocates the required buffer size as instructed + * by the use and then copies the raw data from the current allocated buffer to the new one. This method can become useful if the user wants to insert or + * append data to the raw data, and the previous allocated buffer is too small, so the user wants to allocate a larger buffer and get RawPacket instance to + * point to it + * @param[in] newBufferLength The new buffer length as required by the user. The method is responsible to allocate the memory + * @return True if data was reallocated successfully, false otherwise + */ + virtual bool reallocateData(size_t newBufferLength); + }; + +} // namespace pcpp + +#endif diff --git a/Packet++/header/SSLLayer.h b/Packet++/header/SSLLayer.h index 6a9872b631..592b537765 100644 --- a/Packet++/header/SSLLayer.h +++ b/Packet++/header/SSLLayer.h @@ -1,558 +1,558 @@ -#ifndef PACKETPP_SSL_LAYER -#define PACKETPP_SSL_LAYER - -#include "PointerVector.h" -#include "Layer.h" -#include "SSLCommon.h" -#include "SSLHandshake.h" - -/** - * @file - * This file as well as SSLCommon.h and SSLHandshake.h provide structures that represent SSL/TLS protocol. - * Main features: - * - All common SSL/TLS version are supported from SSL 3.0 to TLS 1.3 - * - All SSL/TLS message types are supported (at least the message types that are not encrypted) - * - More than 300 cipher-suites are supported - * - Only parsing capabilities exist, editing and creation of messages are not supported - * - X509 certificate parsing is not supported - * - *

- * - * __SSL Records:__
- * - * The SSL/TLS protocol has 4 types of records: - * - Handshake record type - * - Change cipher spec record type - * - Alert record type - * - Application data record type - * - * Each record type corresponds to a layer class, and these classes inherit from one base class which is pcpp::SSLLayer. - * The pcpp::SSLLayer is an abstract class which cannot be instantiated. Only its 4 derived classes can be instantiated. - * This means you'll never see a layer of type pcpp::SSLLayer, you'll only see the type of the derived classes. - * A basic class diagram looks like this: - @verbatim - +----------------------------+ - +---| SSLHandshakeLayer | ===> Handshake record type - | +----------------------------+ - | - | +----------------------------+ - +---| SSLChangeCipherSpecLayer | ===> Change cipher spec record type - | +----------------------------+ - | - +------------+ | +----------------------------+ - | SSLLayer |-------------+---| SSLAlertLayer | ===> Alert record type - | (abstract) | | +----------------------------+ - +------------+ | - | +----------------------------+ - +---| SSLApplicationDataLayer | ===> Application data record type - +----------------------------+ - - @endverbatim - * - * A single packet may include several SSL/TLS records, meaning several layer instances of these types, for example: - * - @verbatim - - +--------------------------+ - | EthLayer | - +--------------------------+ - | IPv4Layer | - +--------------------------+ - | TcpLayer | - +--------------------------+ - | SSLHandshakeLayer | \ - +--------------------------+ \ - | SSLChangeCipherSpecLayer | -------- 3 SSL/TLS records in the same packet! - +--------------------------+ / - | SSLHandshakeLayer | / - +--------------------------+ - - @endverbatim - * - *

- * - * __SSL/TLS Handshake records:__
- * - * The SSL/TLS handshake records are the most complex ones. These type of records encapsulate all messages between - * client and server during SSL/TLS connection establishment. To accomplish that a SSL/TLS handshake record holds - * zero or more handshake messages (usually it holds 1 message). These messages form the handshake negotiation between - * the client and the server. There are several types of handshake messages. Some of the are sent from client to server - * and some from server to client. PcapPlusPlus supports 11 of these types (definitely the most common ones). For each - * message there is a designated class which parses the message and exposes its attributes in an easy-to-use manner. - * Here are the list of supported messages: - * - Client-hello - * - Server-hello - * - Certificate - * - Hello-request - * - Server-key-exchange - * - Client-key-exchange - * - Certificate-request - * - Server-hello-done - * - Certificate-verify - * - Finished - * - New-session-ticket - * - * All handshake messages classes inherit from a base abstract class: pcpp::SSLHandshakeMessage which cannot be instantiated. - * Also, all of them reside in SSLHandshake.h. Following is a simple diagram of these classes: - * - @verbatim - - SSLHandshakeMessage - | - +-------------------------------+ |--- SSLClientHelloMessage ==> Client-hello message - | SSLHandshakeLayer | | - +-------------------------------+ |--- SSLServerHelloMessage ==> Server-hello message - | -List of SSLHandshakeMessage | | - | Message1 | |---SSLCertificateMessage ==> Certificate message - | Message2 | | - | ... | |---SSLHelloRequestMessage ==> Hello-request message - | | | - +-------------------------------+ |---SSLServerKeyExchangeMessage ==> Server-key-exchange message - | - |---SSLClientKeyExchangeMessage ==> Client-key-exchange message - | - |---SSLCertificateRequestMessage ==> Certificate-request message - | - |---SSLServerHelloDoneMessage ==> Server-hello-done message - | - |---SSLCertificateVerifyMessage ==> Certificate-verify message - | - |---SSLFinishedMessage ==> Finished message - | - |---SSLNewSessionTicketMessage ==> New-session-ticket message - - @endverbatim - * - * In addition, for all handshake messages which aren't supported in PcapPlusPlus or for encrypted handshake messages - * There is another class: pcpp::SSLUnknownMessage - * - *

- * - * __Cipher suites:__
- * - * Cipher suites are named combinations of authentication, encryption, message authentication code (MAC) and key exchange - * algorithms used to negotiate the security settings for a network connection using SSL/TLS. - * There are many known cipher-suites. PcapPlusPlus support above 300 of them, according to this list: - * http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml - * There is a designated class in PcapPlusPlus called pcpp::SSLCipherSuite which represents the cipher-suites and provides - * access to their attributes. Then there is a static instance of this class for each one of the supported cipher-suites. - * This means there are 300+ static instances of pcpp::SSLCipherSuite representing the different cipher suites. The user can - * access them through static methods in pcpp::SSLCipherSuite or from client-hello and server-hello messages where they appear - * - *

- * - * __SSL/TLS extensions:__
- * - * SSL/TLS handshake messages, specifically client-hello and server-hello usually include extensions. There are various - * types of extensions - some are more broadly used, some are less. In PcapPlusPlus there is a base class for all - * extensions: pcpp::SSLExtension. This class is instantiable and represents a generic extension, which means extension data - * isn't parsed and given to the user as raw data. Currently there are only two extension that are fully parsed which are - * server-name-indication (pcpp::SSLServerNameIndicationExtension) and SupportedVersions (pcpp::SSLSupportedVersionsExtension). - * Both inherit from pcpp::SSLExtension and add additional parsing relevant for the specific extension. - * All other extensions aren't parsed and are represented by instance of pcpp::SSLExtension. - * Access to extensions is done through the handshake messages classes, specifically pcpp::SSLClientHelloMessage and pcpp::SSLServerHelloMessage - */ - - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - - /** - * @class SSLLayer - * The base class for the 4 record type classes. Each record type is represented as a layer. See SSLLayer.h for - * detailed explanation of the TLS/SSL protocol support in PcapPlusPlus. - * This class provides the common functionality used by all record types and also contains static methods for identifying - * an creating SSL/TLS record type layers - */ - class SSLLayer : public Layer - { - public: - - /** - * A static method that checks whether the port is considered as SSL/TLS - * @param[in] port The port number to be checked - */ - static inline bool isSSLPort(uint16_t port); - - /** - * A static methods that gets raw data of a layer and checks whether this data is a SSL/TLS record or not. This check is - * done using the source/dest port and matching of a legal record type in the raw data. The list of ports identified - * as SSL/TLS is hard-coded and includes the following ports: - * - Port 443 [HTTPS] - * - Port 261 [NSIIOPS] - * - Port 448 [DDM-SSL] - * - Port 563 [NNTPS] - * - Port 614 [SSHELL] - * - Port 465 [SMTPS] - * - Port 636 [LDAPS] - * - Port 989 [FTPS - data] - * - Port 990 [FTPS - control] - * - Port 992 [Telnet over TLS/SSL] - * - Port 993 [IMAPS] - * - Port 994 [IRCS] - * - Port 995 [POP3S] - * @param[in] srcPort The source port of the packet that contains the raw data. Source port (or dest port) are a - * criteria to identify SSL/TLS packets - * @param[in] dstPort The dest port of the packet that contains the raw data. Dest port (or source port) are a - * criteria to identify SSL/TLS packets - * @param[in] data The data to check - * @param[in] dataLen Length (in bytes) of the data - * @param[in] ignorePorts SSL/TLS ports are only relevant for parsing the first SSL/TLS message, but are not relevant - * for parsing subsequent messages. This parameter can be set to "true" to skip SSL/TLS ports check. This is an - * optional parameter and its default is "false" - */ - static bool IsSSLMessage(uint16_t srcPort, uint16_t dstPort, uint8_t* data, size_t dataLen, bool ignorePorts = false); - - /** - * A static method that creates SSL/TLS layers by raw data. This method parses the raw data, finds if and which - * SSL/TLS record it is and creates the corresponding record layer. It's the responsibility of the user to free - * the created object when done using it - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - * @return A pointer to the newly created record layer. If no SSL/TLS record could be identified from the raw data - * NULL is returned - */ - static SSLLayer* createSSLMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * Get a pointer to the record header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref ssl_tls_record_layer - */ - ssl_tls_record_layer* getRecordLayer() const { return (ssl_tls_record_layer*)m_Data; } - - /** - * @return The SSL/TLS version used in this record (parsed from the record) - */ - SSLVersion getRecordVersion() const; - - /** - * @return The SSL/TLS record type as parsed from the record - */ - SSLRecordType getRecordType() const; - - // implement abstract methods - - /** - * @return The record size as extracted from the record data (in ssl_tls_record_layer#length) - */ - size_t getHeaderLen() const; - - /** - * 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(); - - OsiModelLayer getOsiModelLayer() const { return OsiModelPresentationLayer; } - - protected: - SSLLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = SSL; } - - }; // class SSLLayer - - - /** - * @class SSLHandshakeLayer - * Represents SSL/TLS handshake layer. This layer may contain one or more handshake messages (all of them inherit from - * the base class SSLHandshakeMessage) which are the SSL/TLS handshake message sent between a client and a server until - * they establish a secure connection (e.g client-hello, server-hello, certificate, client-key-exchange, - * server-key-exchange, etc.). Usually this layer will contain just one message (as the first example below - * demonstrates). But there are cases a layer may contain more than 1 message. To better explain this layer structure - * we'll use 2 examples. The first will be client-hello message. The layer structure will look like this: - @verbatim - - |------------------- SSLHandshakeLayer ----------------------| - +----------------------+-------------------------------------+ - | ssl_tls_record_layer | SSLClientHelloMessage | - | struct | | - +----------------------+-------------------------------------+ - / | \ | \ \ \ - / version \ | handshake \ \ \ - / TLS1_0 \ type \ \ rest of - type \ | SSL_CLIENT_HELLO \ \ message fields... - SSL_HANDSHAKE length handshake \ - (22) xxx | version message - TLS1_2 length - | yyy - @endverbatim - - * Second example is a multiple-message handshake layer comprises of server-hello, certificate and server-key-exchange - * messages: - - @verbatim - - |---------------------------------------------- SSLHandshakeLayer -----------------------------------------------------| - +----------------------+-------------------------------------+---------------------------+-----------------------------+ - | ssl_tls_record_layer | SSLServerHelloMessage | SSLCertificateMessage | SSLServerKeyExchangeMessage | - | struct | | | | - +----------------------+-------------------------------------+---------------------------+-----------------------------+ - / | \ | \ \ | \ | \ - / version \ | handshake \ rest of | | rest | | rest - / TLS1_0 \ type \ message handshake of fields... handshake of fields... - type \ | SSL_SERVER_HELLO \ fields...| type | type - SSL_HANDSHAKE length handshake SSL_CERTIFICATE SSL_SERVER_KEY_EXCHANGE - (22) xxx | version,length | | - - | | | - - @endverbatim - */ - class SSLHandshakeLayer: public SSLLayer - { - public: - - /** - * C'tor for this class that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - SSLHandshakeLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * @return The number of messages in this layer instance - */ - size_t getHandshakeMessagesCount() const { return m_MessageList.size(); } - - /** - * Get a pointer to an handshake message by index. The message are numbered according to their order of appearance - * in the layer. If index is out of bounds (less than 0 or larger than total amount of message) NULL will be - * returned - * @param[in] index The index of the message to return - * @return The pointer to the message object or NULL if index is out of bounds - */ - SSLHandshakeMessage* getHandshakeMessageAt(int index) const; - - /** - * A templated method to get a message of a certain type. If no message of such type is found, NULL is returned - * @return A pointer to the message of the requested type, NULL if not found - */ - template - THandshakeMessage* getHandshakeMessageOfType() const; - - /** - * A templated method to get the first message of a certain type, starting to search from a certain message. - * For example: if the layer looks like: HelloRequest(1) -> HelloRequest(2) - * and the user put HelloRequest(1) as a parameter and wishes to search for an HelloRequest message, the - * HelloRequest(2) will be returned.
- * If no layer of such type is found, NULL is returned - * @param[in] after A pointer to the message to start search from - * @return A pointer to the message of the requested type, NULL if not found - */ - template - THandshakeMessage* getNextHandshakeMessageOfType(const SSLHandshakeMessage* after) const; - - // implement abstract methods - - std::string toString() const; - - /** - * There are no calculated fields for this layer - */ - void computeCalculateFields() {} - - private: - PointerVector m_MessageList; - }; // class SSLHandshakeLayer - - - /** - * @class SSLChangeCipherSpecLayer - * Represents SSL/TLS change-cipher-spec layer. This layer has no additional fields besides common fields described in - * SSLLayer - */ - class SSLChangeCipherSpecLayer : public SSLLayer - { - public: - - /** - * C'tor for this class that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - SSLChangeCipherSpecLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : SSLLayer(data, dataLen, prevLayer, packet) {} - - ~SSLChangeCipherSpecLayer() {} - - // implement abstract methods - - std::string toString() const; - - /** - * There are no calculated fields for this layer - */ - void computeCalculateFields() {} - }; // class SSLChangeCipherSpecLayer - - - /** - * @class SSLAlertLayer - * Represents SSL/TLS alert layer. Inherits from SSLLayer and adds parsing functionality such as retrieving the alert - * level and description - */ - class SSLAlertLayer : public SSLLayer - { - public: - - /** - * C'tor for this class that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - SSLAlertLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : SSLLayer(data, dataLen, prevLayer, packet) {} - - ~SSLAlertLayer() {} - - /** - * @return SSL/TLS alert level. Will return ::SSL_ALERT_LEVEL_ENCRYPTED if alert is encrypted - */ - SSLAlertLevel getAlertLevel() const; - - /** - * @return SSL/TLS alert description. Will return ::SSL_ALERT_ENCRYPTED if alert is encrypted - */ - SSLAlertDescription getAlertDescription(); - - // implement abstract methods - - std::string toString() const; - - /** - * There are no calculated fields for this layer - */ - void computeCalculateFields() {} - }; // class SSLAlertLayer - - - /** - * @class SSLApplicationDataLayer - * Represents SSL/TLS application data layer. This message contains the encrypted data transferred from client to - * server and vice-versa after the SSL/TLS handshake was completed successfully - */ - class SSLApplicationDataLayer : public SSLLayer - { - public: - - /** - * C'tor for this class that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - SSLApplicationDataLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : SSLLayer(data, dataLen, prevLayer, packet) {} - - ~SSLApplicationDataLayer() {} - - /** - * @return A pointer to the encrypted data. This data can be decrypted only if you have the symmetric key - * that was agreed between the client and the server during SSL/TLS handshake process - */ - uint8_t* getEncryptedData() const; - - /** - * @return The length in bytes of the encrypted data returned in getEncryptedData() - */ - size_t getEncryptedDataLen() const; - - // implement abstract methods - - std::string toString() const; - - /** - * There are no calculated fields for this layer - */ - void computeCalculateFields() {} - }; // class SSLApplicationDataLayer - - - template - THandshakeMessage* SSLHandshakeLayer::getHandshakeMessageOfType() const - { - size_t vecSize = m_MessageList.size(); - for (size_t i = 0; i < vecSize; i++) - { - SSLHandshakeMessage* curElem = const_cast(m_MessageList.at(i)); - if (dynamic_cast(curElem) != NULL) - return (THandshakeMessage*)curElem; - } - - // element not found - return NULL; - } // getHandshakeMessageOfType - - - template - THandshakeMessage* SSLHandshakeLayer::getNextHandshakeMessageOfType(const SSLHandshakeMessage* after) const - { - size_t vecSize = m_MessageList.size(); - size_t afterIndex; - - // find the index of "after" - for (afterIndex = 0; afterIndex < vecSize; afterIndex++) - { - SSLHandshakeMessage* curElem = const_cast(m_MessageList.at(afterIndex)); - if (curElem == after) - break; - } - - // "after" not found - if (afterIndex == vecSize) - return NULL; - - for (size_t i = afterIndex+1; i < vecSize; i++) - { - SSLHandshakeMessage* curElem = const_cast(m_MessageList.at(i)); - if (dynamic_cast(curElem) != NULL) - return (THandshakeMessage*)curElem; - } - - // element not found - return NULL; - } // getNextHandshakeMessageOfType - - - // implementation of inline methods - - bool SSLLayer::isSSLPort(uint16_t port) - { - if (port == 443) // HTTPS, this is likely case - return true; - - switch (port) - { - case 261: // NSIIOPS - case 448: // DDM-SSL - case 465: // SMTPS - case 563: // NNTPS - case 614: // SSHELL - case 636: // LDAPS - case 989: // FTPS - data - case 990: // FTPS - control - case 992: // Telnet over TLS/SSL - case 993: // IMAPS - case 994: // IRCS - case 995: // POP3S - return true; - default: - return false; - } - } // isSSLPort - -} // namespace pcpp - -#endif /* PACKETPP_SSL_LAYER */ +#ifndef PACKETPP_SSL_LAYER +#define PACKETPP_SSL_LAYER + +#include "PointerVector.h" +#include "Layer.h" +#include "SSLCommon.h" +#include "SSLHandshake.h" + +/** + * @file + * This file as well as SSLCommon.h and SSLHandshake.h provide structures that represent SSL/TLS protocol. + * Main features: + * - All common SSL/TLS version are supported from SSL 3.0 to TLS 1.3 + * - All SSL/TLS message types are supported (at least the message types that are not encrypted) + * - More than 300 cipher-suites are supported + * - Only parsing capabilities exist, editing and creation of messages are not supported + * - X509 certificate parsing is not supported + * + *

+ * + * __SSL Records:__
+ * + * The SSL/TLS protocol has 4 types of records: + * - Handshake record type + * - Change cipher spec record type + * - Alert record type + * - Application data record type + * + * Each record type corresponds to a layer class, and these classes inherit from one base class which is pcpp::SSLLayer. + * The pcpp::SSLLayer is an abstract class which cannot be instantiated. Only its 4 derived classes can be instantiated. + * This means you'll never see a layer of type pcpp::SSLLayer, you'll only see the type of the derived classes. + * A basic class diagram looks like this: + @verbatim + +----------------------------+ + +---| SSLHandshakeLayer | ===> Handshake record type + | +----------------------------+ + | + | +----------------------------+ + +---| SSLChangeCipherSpecLayer | ===> Change cipher spec record type + | +----------------------------+ + | + +------------+ | +----------------------------+ + | SSLLayer |-------------+---| SSLAlertLayer | ===> Alert record type + | (abstract) | | +----------------------------+ + +------------+ | + | +----------------------------+ + +---| SSLApplicationDataLayer | ===> Application data record type + +----------------------------+ + + @endverbatim + * + * A single packet may include several SSL/TLS records, meaning several layer instances of these types, for example: + * + @verbatim + + +--------------------------+ + | EthLayer | + +--------------------------+ + | IPv4Layer | + +--------------------------+ + | TcpLayer | + +--------------------------+ + | SSLHandshakeLayer | \ + +--------------------------+ \ + | SSLChangeCipherSpecLayer | -------- 3 SSL/TLS records in the same packet! + +--------------------------+ / + | SSLHandshakeLayer | / + +--------------------------+ + + @endverbatim + * + *

+ * + * __SSL/TLS Handshake records:__
+ * + * The SSL/TLS handshake records are the most complex ones. These type of records encapsulate all messages between + * client and server during SSL/TLS connection establishment. To accomplish that a SSL/TLS handshake record holds + * zero or more handshake messages (usually it holds 1 message). These messages form the handshake negotiation between + * the client and the server. There are several types of handshake messages. Some of the are sent from client to server + * and some from server to client. PcapPlusPlus supports 11 of these types (definitely the most common ones). For each + * message there is a designated class which parses the message and exposes its attributes in an easy-to-use manner. + * Here are the list of supported messages: + * - Client-hello + * - Server-hello + * - Certificate + * - Hello-request + * - Server-key-exchange + * - Client-key-exchange + * - Certificate-request + * - Server-hello-done + * - Certificate-verify + * - Finished + * - New-session-ticket + * + * All handshake messages classes inherit from a base abstract class: pcpp::SSLHandshakeMessage which cannot be instantiated. + * Also, all of them reside in SSLHandshake.h. Following is a simple diagram of these classes: + * + @verbatim + + SSLHandshakeMessage + | + +-------------------------------+ |--- SSLClientHelloMessage ==> Client-hello message + | SSLHandshakeLayer | | + +-------------------------------+ |--- SSLServerHelloMessage ==> Server-hello message + | -List of SSLHandshakeMessage | | + | Message1 | |---SSLCertificateMessage ==> Certificate message + | Message2 | | + | ... | |---SSLHelloRequestMessage ==> Hello-request message + | | | + +-------------------------------+ |---SSLServerKeyExchangeMessage ==> Server-key-exchange message + | + |---SSLClientKeyExchangeMessage ==> Client-key-exchange message + | + |---SSLCertificateRequestMessage ==> Certificate-request message + | + |---SSLServerHelloDoneMessage ==> Server-hello-done message + | + |---SSLCertificateVerifyMessage ==> Certificate-verify message + | + |---SSLFinishedMessage ==> Finished message + | + |---SSLNewSessionTicketMessage ==> New-session-ticket message + + @endverbatim + * + * In addition, for all handshake messages which aren't supported in PcapPlusPlus or for encrypted handshake messages + * There is another class: pcpp::SSLUnknownMessage + * + *

+ * + * __Cipher suites:__
+ * + * Cipher suites are named combinations of authentication, encryption, message authentication code (MAC) and key exchange + * algorithms used to negotiate the security settings for a network connection using SSL/TLS. + * There are many known cipher-suites. PcapPlusPlus support above 300 of them, according to this list: + * http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml + * There is a designated class in PcapPlusPlus called pcpp::SSLCipherSuite which represents the cipher-suites and provides + * access to their attributes. Then there is a static instance of this class for each one of the supported cipher-suites. + * This means there are 300+ static instances of pcpp::SSLCipherSuite representing the different cipher suites. The user can + * access them through static methods in pcpp::SSLCipherSuite or from client-hello and server-hello messages where they appear + * + *

+ * + * __SSL/TLS extensions:__
+ * + * SSL/TLS handshake messages, specifically client-hello and server-hello usually include extensions. There are various + * types of extensions - some are more broadly used, some are less. In PcapPlusPlus there is a base class for all + * extensions: pcpp::SSLExtension. This class is instantiable and represents a generic extension, which means extension data + * isn't parsed and given to the user as raw data. Currently there are only two extension that are fully parsed which are + * server-name-indication (pcpp::SSLServerNameIndicationExtension) and SupportedVersions (pcpp::SSLSupportedVersionsExtension). + * Both inherit from pcpp::SSLExtension and add additional parsing relevant for the specific extension. + * All other extensions aren't parsed and are represented by instance of pcpp::SSLExtension. + * Access to extensions is done through the handshake messages classes, specifically pcpp::SSLClientHelloMessage and pcpp::SSLServerHelloMessage + */ + + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + + /** + * @class SSLLayer + * The base class for the 4 record type classes. Each record type is represented as a layer. See SSLLayer.h for + * detailed explanation of the TLS/SSL protocol support in PcapPlusPlus. + * This class provides the common functionality used by all record types and also contains static methods for identifying + * an creating SSL/TLS record type layers + */ + class SSLLayer : public Layer + { + public: + + /** + * A static method that checks whether the port is considered as SSL/TLS + * @param[in] port The port number to be checked + */ + static inline bool isSSLPort(uint16_t port); + + /** + * A static methods that gets raw data of a layer and checks whether this data is a SSL/TLS record or not. This check is + * done using the source/dest port and matching of a legal record type in the raw data. The list of ports identified + * as SSL/TLS is hard-coded and includes the following ports: + * - Port 443 [HTTPS] + * - Port 261 [NSIIOPS] + * - Port 448 [DDM-SSL] + * - Port 563 [NNTPS] + * - Port 614 [SSHELL] + * - Port 465 [SMTPS] + * - Port 636 [LDAPS] + * - Port 989 [FTPS - data] + * - Port 990 [FTPS - control] + * - Port 992 [Telnet over TLS/SSL] + * - Port 993 [IMAPS] + * - Port 994 [IRCS] + * - Port 995 [POP3S] + * @param[in] srcPort The source port of the packet that contains the raw data. Source port (or dest port) are a + * criteria to identify SSL/TLS packets + * @param[in] dstPort The dest port of the packet that contains the raw data. Dest port (or source port) are a + * criteria to identify SSL/TLS packets + * @param[in] data The data to check + * @param[in] dataLen Length (in bytes) of the data + * @param[in] ignorePorts SSL/TLS ports are only relevant for parsing the first SSL/TLS message, but are not relevant + * for parsing subsequent messages. This parameter can be set to "true" to skip SSL/TLS ports check. This is an + * optional parameter and its default is "false" + */ + static bool IsSSLMessage(uint16_t srcPort, uint16_t dstPort, uint8_t* data, size_t dataLen, bool ignorePorts = false); + + /** + * A static method that creates SSL/TLS layers by raw data. This method parses the raw data, finds if and which + * SSL/TLS record it is and creates the corresponding record layer. It's the responsibility of the user to free + * the created object when done using it + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + * @return A pointer to the newly created record layer. If no SSL/TLS record could be identified from the raw data + * NULL is returned + */ + static SSLLayer* createSSLMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * Get a pointer to the record header. Notice this points directly to the data, so every change will change the actual packet data + * @return A pointer to the @ref ssl_tls_record_layer + */ + ssl_tls_record_layer* getRecordLayer() const { return (ssl_tls_record_layer*)m_Data; } + + /** + * @return The SSL/TLS version used in this record (parsed from the record) + */ + SSLVersion getRecordVersion() const; + + /** + * @return The SSL/TLS record type as parsed from the record + */ + SSLRecordType getRecordType() const; + + // implement abstract methods + + /** + * @return The record size as extracted from the record data (in ssl_tls_record_layer#length) + */ + size_t getHeaderLen() const; + + /** + * 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(); + + OsiModelLayer getOsiModelLayer() const { return OsiModelPresentationLayer; } + + protected: + SSLLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) { m_Protocol = SSL; } + + }; // class SSLLayer + + + /** + * @class SSLHandshakeLayer + * Represents SSL/TLS handshake layer. This layer may contain one or more handshake messages (all of them inherit from + * the base class SSLHandshakeMessage) which are the SSL/TLS handshake message sent between a client and a server until + * they establish a secure connection (e.g client-hello, server-hello, certificate, client-key-exchange, + * server-key-exchange, etc.). Usually this layer will contain just one message (as the first example below + * demonstrates). But there are cases a layer may contain more than 1 message. To better explain this layer structure + * we'll use 2 examples. The first will be client-hello message. The layer structure will look like this: + @verbatim + + |------------------- SSLHandshakeLayer ----------------------| + +----------------------+-------------------------------------+ + | ssl_tls_record_layer | SSLClientHelloMessage | + | struct | | + +----------------------+-------------------------------------+ + / | \ | \ \ \ + / version \ | handshake \ \ \ + / TLS1_0 \ type \ \ rest of + type \ | SSL_CLIENT_HELLO \ \ message fields... + SSL_HANDSHAKE length handshake \ + (22) xxx | version message + TLS1_2 length + | yyy + @endverbatim + + * Second example is a multiple-message handshake layer comprises of server-hello, certificate and server-key-exchange + * messages: + + @verbatim + + |---------------------------------------------- SSLHandshakeLayer -----------------------------------------------------| + +----------------------+-------------------------------------+---------------------------+-----------------------------+ + | ssl_tls_record_layer | SSLServerHelloMessage | SSLCertificateMessage | SSLServerKeyExchangeMessage | + | struct | | | | + +----------------------+-------------------------------------+---------------------------+-----------------------------+ + / | \ | \ \ | \ | \ + / version \ | handshake \ rest of | | rest | | rest + / TLS1_0 \ type \ message handshake of fields... handshake of fields... + type \ | SSL_SERVER_HELLO \ fields...| type | type + SSL_HANDSHAKE length handshake SSL_CERTIFICATE SSL_SERVER_KEY_EXCHANGE + (22) xxx | version,length | | + + | | | + + @endverbatim + */ + class SSLHandshakeLayer: public SSLLayer + { + public: + + /** + * C'tor for this class that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + SSLHandshakeLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * @return The number of messages in this layer instance + */ + size_t getHandshakeMessagesCount() const { return m_MessageList.size(); } + + /** + * Get a pointer to an handshake message by index. The message are numbered according to their order of appearance + * in the layer. If index is out of bounds (less than 0 or larger than total amount of message) NULL will be + * returned + * @param[in] index The index of the message to return + * @return The pointer to the message object or NULL if index is out of bounds + */ + SSLHandshakeMessage* getHandshakeMessageAt(int index) const; + + /** + * A templated method to get a message of a certain type. If no message of such type is found, NULL is returned + * @return A pointer to the message of the requested type, NULL if not found + */ + template + THandshakeMessage* getHandshakeMessageOfType() const; + + /** + * A templated method to get the first message of a certain type, starting to search from a certain message. + * For example: if the layer looks like: HelloRequest(1) -> HelloRequest(2) + * and the user put HelloRequest(1) as a parameter and wishes to search for an HelloRequest message, the + * HelloRequest(2) will be returned.
+ * If no layer of such type is found, NULL is returned + * @param[in] after A pointer to the message to start search from + * @return A pointer to the message of the requested type, NULL if not found + */ + template + THandshakeMessage* getNextHandshakeMessageOfType(const SSLHandshakeMessage* after) const; + + // implement abstract methods + + std::string toString() const; + + /** + * There are no calculated fields for this layer + */ + void computeCalculateFields() {} + + private: + PointerVector m_MessageList; + }; // class SSLHandshakeLayer + + + /** + * @class SSLChangeCipherSpecLayer + * Represents SSL/TLS change-cipher-spec layer. This layer has no additional fields besides common fields described in + * SSLLayer + */ + class SSLChangeCipherSpecLayer : public SSLLayer + { + public: + + /** + * C'tor for this class that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + SSLChangeCipherSpecLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : SSLLayer(data, dataLen, prevLayer, packet) {} + + ~SSLChangeCipherSpecLayer() {} + + // implement abstract methods + + std::string toString() const; + + /** + * There are no calculated fields for this layer + */ + void computeCalculateFields() {} + }; // class SSLChangeCipherSpecLayer + + + /** + * @class SSLAlertLayer + * Represents SSL/TLS alert layer. Inherits from SSLLayer and adds parsing functionality such as retrieving the alert + * level and description + */ + class SSLAlertLayer : public SSLLayer + { + public: + + /** + * C'tor for this class that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + SSLAlertLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : SSLLayer(data, dataLen, prevLayer, packet) {} + + ~SSLAlertLayer() {} + + /** + * @return SSL/TLS alert level. Will return ::SSL_ALERT_LEVEL_ENCRYPTED if alert is encrypted + */ + SSLAlertLevel getAlertLevel() const; + + /** + * @return SSL/TLS alert description. Will return ::SSL_ALERT_ENCRYPTED if alert is encrypted + */ + SSLAlertDescription getAlertDescription(); + + // implement abstract methods + + std::string toString() const; + + /** + * There are no calculated fields for this layer + */ + void computeCalculateFields() {} + }; // class SSLAlertLayer + + + /** + * @class SSLApplicationDataLayer + * Represents SSL/TLS application data layer. This message contains the encrypted data transferred from client to + * server and vice-versa after the SSL/TLS handshake was completed successfully + */ + class SSLApplicationDataLayer : public SSLLayer + { + public: + + /** + * C'tor for this class that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + SSLApplicationDataLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : SSLLayer(data, dataLen, prevLayer, packet) {} + + ~SSLApplicationDataLayer() {} + + /** + * @return A pointer to the encrypted data. This data can be decrypted only if you have the symmetric key + * that was agreed between the client and the server during SSL/TLS handshake process + */ + uint8_t* getEncryptedData() const; + + /** + * @return The length in bytes of the encrypted data returned in getEncryptedData() + */ + size_t getEncryptedDataLen() const; + + // implement abstract methods + + std::string toString() const; + + /** + * There are no calculated fields for this layer + */ + void computeCalculateFields() {} + }; // class SSLApplicationDataLayer + + + template + THandshakeMessage* SSLHandshakeLayer::getHandshakeMessageOfType() const + { + size_t vecSize = m_MessageList.size(); + for (size_t i = 0; i < vecSize; i++) + { + SSLHandshakeMessage* curElem = const_cast(m_MessageList.at(i)); + if (dynamic_cast(curElem) != NULL) + return (THandshakeMessage*)curElem; + } + + // element not found + return NULL; + } // getHandshakeMessageOfType + + + template + THandshakeMessage* SSLHandshakeLayer::getNextHandshakeMessageOfType(const SSLHandshakeMessage* after) const + { + size_t vecSize = m_MessageList.size(); + size_t afterIndex; + + // find the index of "after" + for (afterIndex = 0; afterIndex < vecSize; afterIndex++) + { + SSLHandshakeMessage* curElem = const_cast(m_MessageList.at(afterIndex)); + if (curElem == after) + break; + } + + // "after" not found + if (afterIndex == vecSize) + return NULL; + + for (size_t i = afterIndex+1; i < vecSize; i++) + { + SSLHandshakeMessage* curElem = const_cast(m_MessageList.at(i)); + if (dynamic_cast(curElem) != NULL) + return (THandshakeMessage*)curElem; + } + + // element not found + return NULL; + } // getNextHandshakeMessageOfType + + + // implementation of inline methods + + bool SSLLayer::isSSLPort(uint16_t port) + { + if (port == 443) // HTTPS, this is likely case + return true; + + switch (port) + { + case 261: // NSIIOPS + case 448: // DDM-SSL + case 465: // SMTPS + case 563: // NNTPS + case 614: // SSHELL + case 636: // LDAPS + case 989: // FTPS - data + case 990: // FTPS - control + case 992: // Telnet over TLS/SSL + case 993: // IMAPS + case 994: // IRCS + case 995: // POP3S + return true; + default: + return false; + } + } // isSSLPort + +} // namespace pcpp + +#endif /* PACKETPP_SSL_LAYER */ diff --git a/Packet++/header/SdpLayer.h b/Packet++/header/SdpLayer.h index bd26c72fda..9ae16e6559 100644 --- a/Packet++/header/SdpLayer.h +++ b/Packet++/header/SdpLayer.h @@ -1,169 +1,169 @@ -#ifndef PACKETPP_SDP_LAYER -#define PACKETPP_SDP_LAYER - -#include "IpAddress.h" -#include "TextBasedProtocol.h" -#include - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - -/** Protocol version (v) */ -#define PCPP_SDP_PROTOCOL_VERSION_FIELD "v" -/** Originator and session identifier (o) */ -#define PCPP_SDP_ORIGINATOR_FIELD "o" -/** Session name (s) */ -#define PCPP_SDP_SESSION_NAME_FIELD "s" -/** Session title, media title or short information (i) */ -#define PCPP_SDP_INFO_FIELD "i" -/** URI of description (u) */ -#define PCPP_SDP_URI_FIELD "u" -/** Email address with optional name of contacts (e) */ -#define PCPP_SDP_EMAIL_FIELD "e" -/** Phone number with optional name of contacts (p) */ -#define PCPP_SDP_PHONE_FIELD "p" -/** Connection information (c) */ -#define PCPP_SDP_CONNECTION_INFO_FIELD "c" -/** Bandwidth information (b) */ -#define PCPP_SDP_BANDWIDTH_FIELD "b" -/** Time the session is active (t) */ -#define PCPP_SDP_TIME_FIELD "t" -/** Repeat times (r) */ -#define PCPP_SDP_REPEAT_TIMES_FIELD "r" -/** Time zone adjustments (z) */ -#define PCPP_SDP_TIME_ZONE_FIELD "z" -/** Encryption key (k) */ -#define PCPP_SDP_ENCRYPTION_KEY_FIELD "k" -/** Media attribute (a) */ -#define PCPP_SDP_MEDIA_ATTRIBUTE_FIELD "a" -/** Media name and transport address (m) */ -#define PCPP_SDP_MEDIA_NAME_FIELD "m" - - /** - * @class SdpLayer - * Represents a SDP (Session Description Protocol) message. SDP is a text-based protocol described by a series of fields, one per line (lines are separated by CRLF). - * The form of each field is as follows:
- * @code - * [character]=[value] - * @endcode - * Each character represents a certain type of field. All field type are represented as macros in SdpLayer.h file - * (for example: PCPP_SDP_ORIGINATOR_FIELD is a macro for the originator field (o=) ).
- * For more details about SDP structure please refer to its Wikipedia page: https://en.wikipedia.org/wiki/Session_Description_Protocol - */ - class SdpLayer : public TextBasedProtocolMessage - { - public: - - /** A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - SdpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * An empty c'tor which initialize an empty message with no fields - */ - SdpLayer(); - - /** - * A c'tor which initializes a message with the minimum required fields.
- * After this c'tor the message will look like this: - * - * @code - * v=0 - * o=[username] [sessionID] [sessionVersion] IN IP4 [ipAddress] - * s=[sessionName] - * c=IN IP4 [ipAddress] - * t=[startTime] [endTime] - * @endcode - * - * @param[in] username User's login on the originating host - * @param[in] sessionID A globally unique identifier for the session - * @param[in] sessionVersion A version number for this session description - * @param[in] ipAddress The address of the machine from which the session is created - * @param[in] sessionName A textual session name - * @param[in] startTime The start time of the session - * @param[in] stopTime The stop time of the session - */ - SdpLayer(const std::string& username, long sessionID, long sessionVersion, IPv4Address ipAddress, const std::string& sessionName, long startTime, long stopTime); - - ~SdpLayer() {} - - /** - * A copy constructor for this layer. Inherits the base copy constructor and doesn't add - * anything else - * @param[in] other The instance to copy from - */ - SdpLayer(const SdpLayer& other) : TextBasedProtocolMessage(other) {} - - /** - * An assignment operator overload for this layer. Inherits the base assignment operator - * and doesn't add anything else - * @param[in] other The instance to copy from - */ - SdpLayer& operator=(const SdpLayer& other) { TextBasedProtocolMessage::operator=(other); return *this; } - - /** - * The 'originator' field (o=) contains the IP address of the the machine from which the session is created. - * This IP address can be used to track the RTP data relevant for the call. This method extracts this IP address from the 'originator' field and returns it. - * A value of IPv4Address#Zero will be returned in the following cases: (1) if 'originator' field doesn't exist; (2) if it doesn't contain the IP address; - * (3) if it contains a non-IPv4 address - * @return The IP address of the the machine from which the session is created - */ - IPv4Address getOwnerIPv4Address() const; - - /** - * The 'media-description' field (m=) contains the transport port to which the media stream is sent. This port can be used to track the RTP data relevant for the call. - * This method extracts this port from the 'media-description' field and returns it. Since a SDP message can contain several 'media-description' fields, one for each media type - * (e.g audio, image, etc.), the user is required to provide the media type. A value of 0 will be returned in the following cases: (1) if 'media-description' field doesn't - * exist; (2) if provided media type was not found; (3) if 'media-description' field didn't contain a port - * @param[in] mediaType The media type to search in - * @return The transport port to which the media stream is sent - */ - uint16_t getMediaPort(const std::string& mediaType) const; - - /** - * Adds a 'media-description' field (m=) with all necessary data and attribute fields (a=) with data relevant for this media.
- * After this method is run the following block of fields will be added at the end of the message: - * - * @code - * m=[mediaType] [mediaPort] [mediaProtocol] [mediaFormat] - * a=[1st media attribute] - * a=[2nd media attribute] - * ... - * @endcode - * - * @param[in] mediaType The media type, usually "audio", "video", "text" or "image" - * @param[in] mediaPort The transport port to which the media stream is sent - * @param[in] mediaProtocol The transport protocol, usually "udp", "RTP/AVP" or "RTP/SAVP" - * @param[in] mediaFormat A space-separated list of media format description. For example: "8 96" - * @param[in] mediaAttributes A vector of media attributes. Each string in this vector will be - * translated into a 'media-attribute' field (a=) - * @return True if all fields were added properly or false if at least one field was failed to be added - */ - bool addMediaDescription(const std::string& mediaType, uint16_t mediaPort, const std::string& mediaProtocol, const std::string& mediaFormat, std::vector mediaAttributes); - - // overridden methods - - OsiModelLayer getOsiModelLayer() const { return OsiModelSesionLayer; } - - std::string toString() const; - - protected: - - // implementation of abstract methods - char getHeaderFieldNameValueSeparator() const { return '='; } - bool spacesAllowedBetweenHeaderFieldNameAndValue() const { return false; } - - }; -} - -#endif // PACKETPP_SDP_LAYER +#ifndef PACKETPP_SDP_LAYER +#define PACKETPP_SDP_LAYER + +#include "IpAddress.h" +#include "TextBasedProtocol.h" +#include + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + +/** Protocol version (v) */ +#define PCPP_SDP_PROTOCOL_VERSION_FIELD "v" +/** Originator and session identifier (o) */ +#define PCPP_SDP_ORIGINATOR_FIELD "o" +/** Session name (s) */ +#define PCPP_SDP_SESSION_NAME_FIELD "s" +/** Session title, media title or short information (i) */ +#define PCPP_SDP_INFO_FIELD "i" +/** URI of description (u) */ +#define PCPP_SDP_URI_FIELD "u" +/** Email address with optional name of contacts (e) */ +#define PCPP_SDP_EMAIL_FIELD "e" +/** Phone number with optional name of contacts (p) */ +#define PCPP_SDP_PHONE_FIELD "p" +/** Connection information (c) */ +#define PCPP_SDP_CONNECTION_INFO_FIELD "c" +/** Bandwidth information (b) */ +#define PCPP_SDP_BANDWIDTH_FIELD "b" +/** Time the session is active (t) */ +#define PCPP_SDP_TIME_FIELD "t" +/** Repeat times (r) */ +#define PCPP_SDP_REPEAT_TIMES_FIELD "r" +/** Time zone adjustments (z) */ +#define PCPP_SDP_TIME_ZONE_FIELD "z" +/** Encryption key (k) */ +#define PCPP_SDP_ENCRYPTION_KEY_FIELD "k" +/** Media attribute (a) */ +#define PCPP_SDP_MEDIA_ATTRIBUTE_FIELD "a" +/** Media name and transport address (m) */ +#define PCPP_SDP_MEDIA_NAME_FIELD "m" + + /** + * @class SdpLayer + * Represents a SDP (Session Description Protocol) message. SDP is a text-based protocol described by a series of fields, one per line (lines are separated by CRLF). + * The form of each field is as follows:
+ * @code + * [character]=[value] + * @endcode + * Each character represents a certain type of field. All field type are represented as macros in SdpLayer.h file + * (for example: PCPP_SDP_ORIGINATOR_FIELD is a macro for the originator field (o=) ).
+ * For more details about SDP structure please refer to its Wikipedia page: https://en.wikipedia.org/wiki/Session_Description_Protocol + */ + class SdpLayer : public TextBasedProtocolMessage + { + public: + + /** A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + SdpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * An empty c'tor which initialize an empty message with no fields + */ + SdpLayer(); + + /** + * A c'tor which initializes a message with the minimum required fields.
+ * After this c'tor the message will look like this: + * + * @code + * v=0 + * o=[username] [sessionID] [sessionVersion] IN IP4 [ipAddress] + * s=[sessionName] + * c=IN IP4 [ipAddress] + * t=[startTime] [endTime] + * @endcode + * + * @param[in] username User's login on the originating host + * @param[in] sessionID A globally unique identifier for the session + * @param[in] sessionVersion A version number for this session description + * @param[in] ipAddress The address of the machine from which the session is created + * @param[in] sessionName A textual session name + * @param[in] startTime The start time of the session + * @param[in] stopTime The stop time of the session + */ + SdpLayer(const std::string& username, long sessionID, long sessionVersion, IPv4Address ipAddress, const std::string& sessionName, long startTime, long stopTime); + + ~SdpLayer() {} + + /** + * A copy constructor for this layer. Inherits the base copy constructor and doesn't add + * anything else + * @param[in] other The instance to copy from + */ + SdpLayer(const SdpLayer& other) : TextBasedProtocolMessage(other) {} + + /** + * An assignment operator overload for this layer. Inherits the base assignment operator + * and doesn't add anything else + * @param[in] other The instance to copy from + */ + SdpLayer& operator=(const SdpLayer& other) { TextBasedProtocolMessage::operator=(other); return *this; } + + /** + * The 'originator' field (o=) contains the IP address of the the machine from which the session is created. + * This IP address can be used to track the RTP data relevant for the call. This method extracts this IP address from the 'originator' field and returns it. + * A value of IPv4Address#Zero will be returned in the following cases: (1) if 'originator' field doesn't exist; (2) if it doesn't contain the IP address; + * (3) if it contains a non-IPv4 address + * @return The IP address of the the machine from which the session is created + */ + IPv4Address getOwnerIPv4Address() const; + + /** + * The 'media-description' field (m=) contains the transport port to which the media stream is sent. This port can be used to track the RTP data relevant for the call. + * This method extracts this port from the 'media-description' field and returns it. Since a SDP message can contain several 'media-description' fields, one for each media type + * (e.g audio, image, etc.), the user is required to provide the media type. A value of 0 will be returned in the following cases: (1) if 'media-description' field doesn't + * exist; (2) if provided media type was not found; (3) if 'media-description' field didn't contain a port + * @param[in] mediaType The media type to search in + * @return The transport port to which the media stream is sent + */ + uint16_t getMediaPort(const std::string& mediaType) const; + + /** + * Adds a 'media-description' field (m=) with all necessary data and attribute fields (a=) with data relevant for this media.
+ * After this method is run the following block of fields will be added at the end of the message: + * + * @code + * m=[mediaType] [mediaPort] [mediaProtocol] [mediaFormat] + * a=[1st media attribute] + * a=[2nd media attribute] + * ... + * @endcode + * + * @param[in] mediaType The media type, usually "audio", "video", "text" or "image" + * @param[in] mediaPort The transport port to which the media stream is sent + * @param[in] mediaProtocol The transport protocol, usually "udp", "RTP/AVP" or "RTP/SAVP" + * @param[in] mediaFormat A space-separated list of media format description. For example: "8 96" + * @param[in] mediaAttributes A vector of media attributes. Each string in this vector will be + * translated into a 'media-attribute' field (a=) + * @return True if all fields were added properly or false if at least one field was failed to be added + */ + bool addMediaDescription(const std::string& mediaType, uint16_t mediaPort, const std::string& mediaProtocol, const std::string& mediaFormat, std::vector mediaAttributes); + + // overridden methods + + OsiModelLayer getOsiModelLayer() const { return OsiModelSesionLayer; } + + std::string toString() const; + + protected: + + // implementation of abstract methods + char getHeaderFieldNameValueSeparator() const { return '='; } + bool spacesAllowedBetweenHeaderFieldNameAndValue() const { return false; } + + }; +} + +#endif // PACKETPP_SDP_LAYER diff --git a/Packet++/header/SipLayer.h b/Packet++/header/SipLayer.h index de0ae7f1b7..8d646d5486 100644 --- a/Packet++/header/SipLayer.h +++ b/Packet++/header/SipLayer.h @@ -1,677 +1,677 @@ -#ifndef PACKETPP_SIP_LAYER -#define PACKETPP_SIP_LAYER - -#include "TextBasedProtocol.h" - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ -// some popular SIP header fields - -/** From field */ -#define PCPP_SIP_FROM_FIELD "From" -/** To field */ -#define PCPP_SIP_TO_FIELD "To" -/** Via field */ -#define PCPP_SIP_VIA_FIELD "Via" -/** Call-ID field */ -#define PCPP_SIP_CALL_ID_FIELD "Call-ID" -/** Content-Type field */ -#define PCPP_SIP_CONTENT_TYPE_FIELD "Content-Type" -/** Content-Length field */ -#define PCPP_SIP_CONTENT_LENGTH_FIELD "Content-Length" -/** Content-Disposition field */ -#define PCPP_SIP_CONTENT_DISPOSITION_FIELD "Content-Disposition" -/** Content-Encoding field */ -#define PCPP_SIP_CONTENT_ENCODING_FIELD "Content-Encoding" -/** Content-Language field */ -#define PCPP_SIP_CONTENT_LANGUAGE_FIELD "Content-Language" -/** CSeq field */ -#define PCPP_SIP_CSEQ_FIELD "CSeq" -/** Contact field */ -#define PCPP_SIP_CONTACT_FIELD "Contact" -/** Max-Forwards field */ -#define PCPP_SIP_MAX_FORWARDS_FIELD "Max-Forwards" -/** User-Agent field */ -#define PCPP_SIP_USER_AGENT_FIELD "User-Agent" -/** Accept field */ -#define PCPP_SIP_ACCEPT_FIELD "Accept" -/** Accept-Encoding field */ -#define PCPP_SIP_ACCEPT_ENCODING_FIELD "Accept-Encoding" -/** Accept-Language field */ -#define PCPP_SIP_ACCEPT_LANGUAGE_FIELD "Accept-Language" -/** Allow field */ -#define PCPP_SIP_ALLOW_FIELD "Allow" -/** Authorization field */ -#define PCPP_SIP_AUTHORIZATION_FIELD "Authorization" -/** Date field */ -#define PCPP_SIP_DATE_FIELD "Date" -/** MIME-Version field */ -#define PCPP_SIP_MIME_VERSION_FIELD "MIME-Version" -/** Reason field */ -#define PCPP_SIP_REASON_FIELD "Reason" -/** Supported field */ -#define PCPP_SIP_SUPPORTED_FIELD "Supported" -/** Server field */ -#define PCPP_SIP_SERVER_FIELD "Server" -/** WWW-Authenticate fild */ -#define PCPP_SIP_WWW_AUTHENTICATE_FIELD "WWW-Authenticate" -/** Retry-After field */ -#define PCPP_SIP_RETRY_AFTER_FIELD "Retry-After" -/** Record-Route field */ -#define PCPP_SIP_RECORD_ROUTE_FIELD "Record-Route" - - - /** - * @class SipLayer - * Represents a general SIP message. It's an abstract class and cannot be instantiated. It's inherited by SipRequestLayer and SipResponseLayer - */ - class SipLayer : public TextBasedProtocolMessage - { - public: - - /** - * The length of the body of many SIP response messages is determined by a SIP header field called "Content-Length". This method - * parses this field, extracts its value and return it. If this field doesn't exist 0 is returned - * @return SIP response body length determined by "Content-Length" field - */ - int getContentLength() const; - - /** - * The length of the body of many SIP messages is determined by a header field called "Content-Length". This method sets - * The content-length field value. The method supports several cases: - * - If the "Content-Length" field exists - the method will only replace the existing value with the new value - * - If the "Content-Length" field doesn't exist - the method will create this field and put the value in it. Here are also 2 cases: - * - If prevFieldName is specified - the new "Content-Length" field will be created after it - * - If prevFieldName isn't specified or doesn't exist - the new "Content-Length" field will be created as the last field before - * end-of-header field - * - * @param[in] contentLength The content length value to set - * @param[in] prevFieldName Optional parameter, if specified and "Content-Length" field doesn't exist, it will be created after this field - * @return A pointer to the "Content-Length" field, or NULL if creation failed - */ - HeaderField* setContentLength(int contentLength, const std::string &prevFieldName = ""); - - // Overridden methods - - OsiModelLayer getOsiModelLayer() const { 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(); - - /** - * 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(); - - /** - * A static method that checks whether the port is considered as SIP - * @param[in] port The port number to be checked - */ - static bool isSipPort(uint16_t port) { return port == 5060 || port == 5061; } - - protected: - SipLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : TextBasedProtocolMessage(data, dataLen, prevLayer, packet) {} - SipLayer() : TextBasedProtocolMessage() {} - SipLayer(const SipLayer& other) : TextBasedProtocolMessage(other) {} - SipLayer& operator=(const SipLayer& other) { TextBasedProtocolMessage::operator=(other); return *this; } - - // implementation of abstract methods - char getHeaderFieldNameValueSeparator() const { return ':'; } - bool spacesAllowedBetweenHeaderFieldNameAndValue() const { return true; } - }; - - - class SipRequestFirstLine; - - - /** - * @class SipRequestLayer - * Represents a SIP request header and inherits all basic functionality of SipLayer and TextBasedProtocolMessage. - * The functionality that is added for this class is the SIP first line concept. A SIP request has the following first line: - * INVITE sip:bla@bla.com:12345 SIP/2.0 - * Since it's not an "ordinary" header field, it requires a special treatment and gets a class of it's own: SipRequestFirstLine. - * In most cases a SIP request will be contained in a single packet but for cases it is not, only the first packet will be identified as SIP - * request layer. You can find out whether the header is complete by using SipLayer#isHeaderComplete() - */ - class SipRequestLayer : public SipLayer - { - friend class SipRequestFirstLine; - - public: - /** - * SIP request methods - */ - enum SipMethod - { - /** INVITE */ - SipINVITE, - /** ACK */ - SipACK, - /** BYE */ - SipBYE, - /** CANCEL */ - SipCANCEL, - /** REFISTER */ - SipREGISTER, - /** PRACK */ - SipPRACK, - /** OPTIONS */ - SipOPTIONS, - /** SUBSCRIBE */ - SipSUBSCRIBE, - /** NOTIFY */ - SipNOTIFY, - /** PUBLISH */ - SipPUBLISH, - /** INFO */ - SipINFO, - /** REFER */ - SipREFER, - /** MESSAGE */ - SipMESSAGE, - /** UPDATE */ - SipUPDATE, - /** Unknown SIP method */ - SipMethodUnknown - }; - - /** A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - SipRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that allocates a new SIP request with only the first line filled. The request will be created without further fields. - * The user can then add fields using addField() or insertField() methods - * @param[in] method The SIP method to be used in this SIP request - * @param[in] requestUri The URI of the request - * @param[in] version SIP version to be used in this request. Default is "SIP/2.0" - */ - SipRequestLayer(SipMethod method, const std::string& requestUri, const std::string& version = "SIP/2.0"); - - ~SipRequestLayer(); - - /** - * A copy constructor for this layer. Inherits base copy constructor SipLayer and adds the functionality - * of copying the first line - * @param[in] other The instance to copy from - */ - SipRequestLayer(const SipRequestLayer& other); - - /** - * An assignment operator overload for this layer. This method inherits base assignment operator SipLayer#operator=() and adds the functionality - * of copying the first line - * @param[in] other The instance to copy from - */ - SipRequestLayer& operator=(const SipRequestLayer& other); - - /** - * @return A pointer to the first line instance for this message - */ - SipRequestFirstLine* getFirstLine() const { return m_FirstLine; } - - // implement Layer's abstract methods - - std::string toString() const; - - private: - SipRequestFirstLine* m_FirstLine; - }; - - - - - class SipResponseFirstLine; - - - /** - * @class SipResponseLayer - * Represents an SIP response message and inherits all basic functionality of SipLayer and TextBasedProtocolMessage. - * The functionality that is added for this class is the SIP first line concept. A SIP response has the following first line: - * 200 OK SIP/2.0 - * Since it's not an "ordinary" header field, it requires a special treatment and gets a class of it's own: SipResponseFirstLine. - * In most cases a SIP response will be contained in a single packet but for cases it is not, only the first packet will be identified as SIP - * response layer. You can find out whether the header is complete by using SipLayer#isHeaderComplete() - */ - class SipResponseLayer : public SipLayer - { - friend class SipResponseFirstLine; - public: - - /** - * Enum for SIP response status codes. List is taken from Wikipedia: https://en.wikipedia.org/wiki/List_of_SIP_response_codes - */ - enum SipResponseStatusCode - { - /** Extended search being performed may take a significant time so a forking proxy must send a 100 Trying response */ - Sip100Trying, - /** Destination user agent received INVITE, and is alerting user of call */ - Sip180Ringing, - /** Servers can optionally send this response to indicate a call is being forwarded */ - Sip181CallisBeingForwarded, - /** Indicates that the destination was temporarily unavailable, so the server has queued the call until the destination is available. A server may send multiple 182 responses to update progress of the queue */ - Sip182Queued, - /** This response may be used to send extra information for a call which is still being set up */ - Sip183SessioninProgress, - /** Can be used by User Agent Server to indicate to upstream SIP entities (including the User Agent Client (UAC)) that an early dialog has been terminated */ - Sip199EarlyDialogTerminated, - /** Indicates the request was successful */ - Sip200OK, - /** Indicates that the request has been accepted for processing, but the processing has not been completed */ - Sip202Accepted, - /** Indicates the request was successful, but the corresponding response will not be received */ - Sip204NoNotification, - /** The address resolved to one of several options for the user or client to choose between, which are listed in the message body or the message's Contact fields */ - Sip300MultipleChoices, - /** The original Request-URI is no longer valid, the new address is given in the Contact header field, and the client should update any records of the original Request-URI with the new value */ - Sip301MovedPermanently, - /** The client should try at the address in the Contact field. If an Expires field is present, the client may cache the result for that period of time */ - Sip302MovedTemporarily, - /** The Contact field details a proxy that must be used to access the requested destination */ - Sip305UseProxy, - /** The call failed, but alternatives are detailed in the message body */ - Sip380AlternativeService, - /** The request could not be understood due to malformed syntax */ - Sip400BadRequest, - /** The request requires user authentication. This response is issued by UASs and registrars */ - Sip401Unauthorized, - /** Reserved for future use */ - Sip402PaymentRequired, - /** The server understood the request, but is refusing to fulfill it */ - Sip403Forbidden, - /** The server has definitive information that the user does not exist at the domain specified in the Request-URI. This status is also returned if the domain in the Request-URI does not match any of the domains handled by the recipient of the request */ - Sip404NotFound, - /** The method specified in the Request-Line is understood, but not allowed for the address identified by the Request-URI */ - Sip405MethodNotAllowed, - /** The resource identified by the request is only capable of generating response entities that have content characteristics but not acceptable according to the Accept header field sent in the request */ - Sip406NotAcceptable, - /** The request requires user authentication. This response is issued by proxys */ - Sip407ProxyAuthenticationRequired, - /** Couldn't find the user in time. The server could not produce a response within a suitable amount of time, for example, if it could not determine the location of the user in time. The client MAY repeat the request without modifications at any later time */ - Sip408RequestTimeout, - /** User already registered */ - Sip409Conflict, - /** The user existed once, but is not available here any more */ - Sip410Gone, - /** The server will not accept the request without a valid Content-Length */ - Sip411LengthRequired, - /** The given precondition has not been met */ - Sip412ConditionalRequestFailed, - /** Request body too large */ - Sip413RequestEntityTooLarge, - /** The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret */ - Sip414RequestURITooLong, - /** Request body in a format not supported */ - Sip415UnsupportedMediaType, - /** Request-URI is unknown to the server */ - Sip416UnsupportedURIScheme, - /** There was a resource-priority option tag, but no Resource-Priority header */ - Sip417UnknownResourcePriority, - /** Bad SIP Protocol Extension used, not understood by the server */ - Sip420BadExtension, - /** The server needs a specific extension not listed in the Supported header */ - Sip421ExtensionRequired, - /** The received request contains a Session-Expires header field with a duration below the minimum timer */ - Sip422SessionIntervalTooSmall, - /** Expiration time of the resource is too short */ - Sip423IntervalTooBrief, - /** The request's location content was malformed or otherwise unsatisfactory */ - Sip424BadLocationInformation, - /** The server policy requires an Identity header, and one has not been provided */ - Sip428UseIdentityHeader, - /** The server did not receive a valid Referred-By token on the request */ - Sip429ProvideReferrerIdentity, - /** A specific flow to a user agent has failed, although other flows may succeed. This response is intended for use between proxy devices, and should not be seen by an endpoint (and if it is seen by one, should be treated as a 400 Bad Request response) */ - Sip430FlowFailed, - /** The request has been rejected because it was anonymous */ - Sip433AnonymityDisallowed, - /** The request has an Identity-Info header, and the URI scheme in that header cannot be dereferenced */ - Sip436BadIdentityInfo, - /** The server was unable to validate a certificate for the domain that signed the request */ - Sip437UnsupportedCertificate, - /** The server obtained a valid certificate that the request claimed was used to sign the request, but was unable to verify that signature */ - Sip438InvalidIdentityHeader, - /** The first outbound proxy the user is attempting to register through does not support the "outbound" feature of RFC 5626, although the registrar does */ - Sip439FirstHopLacksOutboundSupport, - /** If a SIP proxy determines a response context has insufficient Incoming Max-Breadth to carry out a desired parallel fork, and the proxy is unwilling/unable to compensate by forking serially or sending a redirect, that proxy MUST return a 440 response. A client receiving a 440 response can infer that its request did not reach all possible destinations */ - Sip440MaxBreadthExceeded, - /** If a SIP UA receives an INFO request associated with an Info Package that the UA has not indicated willingness to receive, the UA MUST send a 469 response, which contains a Recv-Info header field with Info Packages for which the UA is willing to receive INFO requests */ - Sip469BadInfoPackage, - /** The source of the request did not have the permission of the recipient to make such a request */ - Sip470ConsentNeeded, - /** Callee currently unavailable */ - Sip480TemporarilyUnavailable, - /** Server received a request that does not match any dialog or transaction */ - Sip481Call_TransactionDoesNotExist, - /** Server has detected a loop */ - Sip482LoopDetected, - /** Max-Forwards header has reached the value '0' */ - Sip483TooManyHops, - /** Request-URI incomplete */ - Sip484AddressIncomplete, - /** Request-URI is ambiguous */ - Sip485Ambiguous, - /** Callee is busy */ - Sip486BusyHere, - /** Request has terminated by bye or cancel */ - Sip487RequestTerminated, - /** Some aspect of the session description or the Request-URI is not acceptable */ - Sip488NotAcceptableHere, - /** The server did not understand an event package specified in an Event header field */ - Sip489BadEvent, - /** Server has some pending request from the same dialog */ - Sip491RequestPending, - /** Request contains an encrypted MIME body, which recipient can not decrypt */ - Sip493Undecipherable, - /** The server has received a request that requires a negotiated security mechanism, and the response contains a list of suitable security mechanisms for the requester to choose between, or a digest authentication challenge */ - Sip494SecurityAgreementRequired, - /** The server could not fulfill the request due to some unexpected condition */ - Sip500ServerInternalError, - /** The server does not have the ability to fulfill the request, such as because it does not recognize the request method. (Compare with 405 Method Not Allowed, where the server recognizes the method but does not allow or support it.) */ - Sip501NotImplemented, - /** The server is acting as a gateway or proxy, and received an invalid response from a downstream server while attempting to fulfill the request */ - Sip502BadGateway, - /** The server is undergoing maintenance or is temporarily overloaded and so cannot process the request. A "Retry-After" header field may specify when the client may reattempt its request */ - Sip503ServiceUnavailable, - /** The server attempted to access another server in attempting to process the request, and did not receive a prompt response */ - Sip504ServerTimeout, - /** The SIP protocol version in the request is not supported by the server */ - Sip505VersionNotSupported, - /** The request message length is longer than the server can process */ - Sip513MessageTooLarge, - /** The server is unable or unwilling to meet some constraints specified in the offer */ - Sip580PreconditionFailure, - /** All possible destinations are busy. Unlike the 486 response, this response indicates the destination knows there are no alternative destinations (such as a voicemail server) able to accept the call */ - Sip600BusyEverywhere, - /** The destination does not wish to participate in the call, or cannot do so, and additionally the destination knows there are no alternative destinations (such as a voicemail server) willing to accept the call */ - Sip603Decline, - /** The server has authoritative information that the requested user does not exist anywhere */ - Sip604DoesNotExistAnywhere, - /** The user's agent was contacted successfully but some aspects of the session description such as the requested media, bandwidth, or addressing style were not acceptable */ - Sip606NotAcceptable, - /** The called party did not want this call from the calling party. Future attempts from the calling party are likely to be similarly rejected */ - Sip607Unwanted, - /** Unknown SIP status code */ - SipStatusCodeUnknown - }; - - /** A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - SipResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that allocates a new SIP response with only the first line filled. The request will be created without further fields. - * The user can then add fields using addField() or insertField() methods - * @param[in] statusCode SIP status code to set - * @param[in] statusCodeString Most status codes have their default string, e.g 200 is usually "OK" etc. - * But the user can set a non-default status code string and it will be written in the header first line. Empty string ("") means using the - * default status code string. Also, the default is using the default status code string - * @param[in] sipVersion SIP version to set, default is SIP/2.0 - * - */ - explicit SipResponseLayer(SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString = "", const std::string& sipVersion = "SIP/2.0"); - - virtual ~SipResponseLayer(); - - /** - * A copy constructor for this layer. This copy constructor inherits base copy constructor SipLayer and adds the functionality - * of copying the first line as well - * @param[in] other The instance to copy from - */ - SipResponseLayer(const SipResponseLayer& other); - - /** - * An assignment operator overload for this layer. This method inherits base assignment operator SipLayer#operator=() and adds the functionality - * of copying the first line as well - * @param[in] other The instance to copy from - */ - SipResponseLayer& operator=(const SipResponseLayer& other); - - /** - * @return A pointer to the first line instance for this message - */ - SipResponseFirstLine* getFirstLine() const { return m_FirstLine; } - - // implement Layer's abstract methods - - std::string toString() const; - - private: - SipResponseFirstLine* m_FirstLine; - }; - - - - /** - * @class SipRequestFirstLine - * Represents an SIP request first line. The first line includes 3 parameters: SIP method (e.g INVITE, ACK, BYE, etc.), - * URI (e.g sip:bla@bla.com:12345) and SIP version (usually SIP/2.0). All these parameters are included in this class, and the user - * can retrieve or set them. - * This class cannot be instantiated by users, it's created inside SipRequestLayer and user can get a pointer to an instance of it. All "getters" - * of this class retrieve the actual data of the SIP request and the "setters" actually change the packet data. - * Since SIP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So many "setter" methods - * of this class may need to shorten or extend the data in SipRequestLayer. These methods will return a false value if this action failed - */ - class SipRequestFirstLine - { - friend class SipRequestLayer; - public: - - /** - * @return The SIP request method - */ - SipRequestLayer::SipMethod getMethod() const { return m_Method; } - - /** - * Set the SIP request method - * @param[in] newMethod The method to set - * @return False if newMethod is SipRequestLayer#SipMethodUnknown or if shortening/extending the SipRequestLayer data failed. True otherwise - */ - bool setMethod(SipRequestLayer::SipMethod newMethod); - - /** - * @return A copied version of the URI (notice changing the return value won't change the actual data of the packet) - */ - std::string getUri() const; - - /** - * Set the URI - * @param[in] newUri The URI to set - * @return False if shortening/extending the SipRequestLayer data failed. True otherwise - */ - bool setUri(const std::string& newUri); - - /** - * @return The SIP version - */ - std::string getVersion() const { return m_Version; } - - /** - * A static method for parsing the SIP method out of raw data - * @param[in] data The raw data - * @param[in] dataLen The raw data length - * @return The parsed SIP method - */ - static SipRequestLayer::SipMethod parseMethod(char* data, size_t dataLen); - - /** - * @return The size in bytes of the SIP request first line - */ - int getSize() const { return m_FirstLineEndOffset; } - - /** - * As explained in SipRequestLayer, a SIP message can sometimes spread over more than 1 packet, so when looking at a single packet - * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication - * whether the first line is partial - * @return False if the first line is partial, true if it's complete - */ - bool isComplete() const { return m_IsComplete; } - - /** - * @class SipRequestFirstLineException - * This exception can be thrown while constructing SipRequestFirstLine (the constructor is private, so the construction happens - * only in SipRequestLayer). This kind of exception is thrown if trying to construct with SIP method of - * SipRequestLayer#SipMethodUnknown or with empty SIP version - */ - class SipRequestFirstLineException : public std::exception - { - public: - ~SipRequestFirstLineException() throw() {} - void setMessage(const std::string &message) { m_Message = message; } - virtual const char* what() const throw() - { - return m_Message.c_str(); - } - private: - std::string m_Message; - }; - - private: - SipRequestFirstLine(SipRequestLayer* sipRequest); - SipRequestFirstLine(SipRequestLayer* sipRequest, SipRequestLayer::SipMethod method, const std::string& version, const std::string& uri); - //throw(SipRequestFirstLineException); // Deprecated in C++17 - - void parseVersion(); - - SipRequestLayer* m_SipRequest; - SipRequestLayer::SipMethod m_Method; - std::string m_Version; - int m_VersionOffset; - int m_UriOffset; - int m_FirstLineEndOffset; - bool m_IsComplete; - SipRequestFirstLineException m_Exception; - }; - - - - - /** - * @class SipResponseFirstLine - * Represents an SIP response message first line. The first line includes 2 parameters: status code (e.g 100 Trying ,200 OK, etc.), - * and SIP version (usually SIP/2.0). These 2 parameters are included in this class, and the user can retrieve or set them. - * This class cannot be instantiated by users, it's created inside SipResponseLayer and user can get a pointer to an instance of it. The "getter" - * methods of this class will retrieve the actual data of the SIP response and the "setter" methods will change the packet data. - * Since SIP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So most "setter" methods - * of this class may need to shorten or extend the data in SipResponseLayer. These methods will return a false value if this action failed - */ - class SipResponseFirstLine - { - friend class SipResponseLayer; - public: - /** - * @return The status code as SipResponseLayer#SipResponseStatusCode enum - */ - SipResponseLayer::SipResponseStatusCode getStatusCode() const { return m_StatusCode; } - - /** - * @return The status code number as integer (e.g 200, 100, etc.) - */ - int getStatusCodeAsInt() const; - - /** - * @return The status code message (e.g "OK", "Trying", etc.) - */ - std::string getStatusCodeString() const; - - /** - * Set the status code - * @param[in] newStatusCode The new status code to set - * @param[in] statusCodeString An optional parameter: set a non-default status code message (e.g "Bla Bla" instead of "Not Found"). If - * this parameter isn't supplied or supplied as empty string (""), the default message for the status code will be set - */ - bool setStatusCode(SipResponseLayer::SipResponseStatusCode newStatusCode, std::string statusCodeString = ""); - - /** - * @return The SIP version - */ - std::string getVersion() const { return m_Version; } - - /** - * Set the SIP version. The version to set is expected to be in the format of SIP/x.y otherwise an error will be written to log - * @param[in] newVersion The SIP version to set - */ - void setVersion(const std::string& newVersion); - - /** - * A static method for parsing the SIP status code out of raw data - * @param[in] data The raw data - * @param[in] dataLen The raw data length - * @return The parsed SIP status code as enum - */ - static SipResponseLayer::SipResponseStatusCode parseStatusCode(char* data, size_t dataLen); - - /** - * A static method for parsing the SIP version out of raw data - * @param[in] data The raw data - * @param[in] dataLen The raw data length - * @return The parsed SIP version string or an empty string if version cannot be extracted - */ - static std::string parseVersion(char* data, size_t dataLen); - - /** - * @return The size in bytes of the SIP response first line - */ - int getSize() const { return m_FirstLineEndOffset; } - - /** - * As explained in SipResponseLayer, A SIP message can sometimes spread over more than 1 packet, so when looking at a single packet - * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication - * whether the first line is partial - * @return False if the first line is partial, true if it's complete - */ - bool isComplete() const { return m_IsComplete; } - - /** - * @class SipResponseFirstLineException - * This exception can be thrown while constructing SipResponseFirstLine (the constructor is private, so the construction happens - * only in SipResponseLayer). This kind of exception will be thrown if trying to construct with SIP status code of - * SipResponseLayer#SipStatusCodeUnknown or with an empty SIP version - */ - class SipResponseFirstLineException : public std::exception - { - public: - ~SipResponseFirstLineException() throw() {} - void setMessage(const std::string &message) { m_Message = message; } - virtual const char* what() const throw() - { - return m_Message.c_str(); - } - private: - std::string m_Message; - }; - - private: - SipResponseFirstLine(SipResponseLayer* sipResponse); - SipResponseFirstLine(SipResponseLayer* sipResponse, const std::string& version, SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString = ""); - - static SipResponseLayer::SipResponseStatusCode validateStatusCode(char* data, size_t dataLen, SipResponseLayer::SipResponseStatusCode potentialCode); - - - SipResponseLayer* m_SipResponse; - std::string m_Version; - SipResponseLayer::SipResponseStatusCode m_StatusCode; - int m_FirstLineEndOffset; - bool m_IsComplete; - SipResponseFirstLineException m_Exception; - }; - -} - -#endif // PACKETPP_SIP_LAYER +#ifndef PACKETPP_SIP_LAYER +#define PACKETPP_SIP_LAYER + +#include "TextBasedProtocol.h" + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ +// some popular SIP header fields + +/** From field */ +#define PCPP_SIP_FROM_FIELD "From" +/** To field */ +#define PCPP_SIP_TO_FIELD "To" +/** Via field */ +#define PCPP_SIP_VIA_FIELD "Via" +/** Call-ID field */ +#define PCPP_SIP_CALL_ID_FIELD "Call-ID" +/** Content-Type field */ +#define PCPP_SIP_CONTENT_TYPE_FIELD "Content-Type" +/** Content-Length field */ +#define PCPP_SIP_CONTENT_LENGTH_FIELD "Content-Length" +/** Content-Disposition field */ +#define PCPP_SIP_CONTENT_DISPOSITION_FIELD "Content-Disposition" +/** Content-Encoding field */ +#define PCPP_SIP_CONTENT_ENCODING_FIELD "Content-Encoding" +/** Content-Language field */ +#define PCPP_SIP_CONTENT_LANGUAGE_FIELD "Content-Language" +/** CSeq field */ +#define PCPP_SIP_CSEQ_FIELD "CSeq" +/** Contact field */ +#define PCPP_SIP_CONTACT_FIELD "Contact" +/** Max-Forwards field */ +#define PCPP_SIP_MAX_FORWARDS_FIELD "Max-Forwards" +/** User-Agent field */ +#define PCPP_SIP_USER_AGENT_FIELD "User-Agent" +/** Accept field */ +#define PCPP_SIP_ACCEPT_FIELD "Accept" +/** Accept-Encoding field */ +#define PCPP_SIP_ACCEPT_ENCODING_FIELD "Accept-Encoding" +/** Accept-Language field */ +#define PCPP_SIP_ACCEPT_LANGUAGE_FIELD "Accept-Language" +/** Allow field */ +#define PCPP_SIP_ALLOW_FIELD "Allow" +/** Authorization field */ +#define PCPP_SIP_AUTHORIZATION_FIELD "Authorization" +/** Date field */ +#define PCPP_SIP_DATE_FIELD "Date" +/** MIME-Version field */ +#define PCPP_SIP_MIME_VERSION_FIELD "MIME-Version" +/** Reason field */ +#define PCPP_SIP_REASON_FIELD "Reason" +/** Supported field */ +#define PCPP_SIP_SUPPORTED_FIELD "Supported" +/** Server field */ +#define PCPP_SIP_SERVER_FIELD "Server" +/** WWW-Authenticate fild */ +#define PCPP_SIP_WWW_AUTHENTICATE_FIELD "WWW-Authenticate" +/** Retry-After field */ +#define PCPP_SIP_RETRY_AFTER_FIELD "Retry-After" +/** Record-Route field */ +#define PCPP_SIP_RECORD_ROUTE_FIELD "Record-Route" + + + /** + * @class SipLayer + * Represents a general SIP message. It's an abstract class and cannot be instantiated. It's inherited by SipRequestLayer and SipResponseLayer + */ + class SipLayer : public TextBasedProtocolMessage + { + public: + + /** + * The length of the body of many SIP response messages is determined by a SIP header field called "Content-Length". This method + * parses this field, extracts its value and return it. If this field doesn't exist 0 is returned + * @return SIP response body length determined by "Content-Length" field + */ + int getContentLength() const; + + /** + * The length of the body of many SIP messages is determined by a header field called "Content-Length". This method sets + * The content-length field value. The method supports several cases: + * - If the "Content-Length" field exists - the method will only replace the existing value with the new value + * - If the "Content-Length" field doesn't exist - the method will create this field and put the value in it. Here are also 2 cases: + * - If prevFieldName is specified - the new "Content-Length" field will be created after it + * - If prevFieldName isn't specified or doesn't exist - the new "Content-Length" field will be created as the last field before + * end-of-header field + * + * @param[in] contentLength The content length value to set + * @param[in] prevFieldName Optional parameter, if specified and "Content-Length" field doesn't exist, it will be created after this field + * @return A pointer to the "Content-Length" field, or NULL if creation failed + */ + HeaderField* setContentLength(int contentLength, const std::string &prevFieldName = ""); + + // Overridden methods + + OsiModelLayer getOsiModelLayer() const { 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(); + + /** + * 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(); + + /** + * A static method that checks whether the port is considered as SIP + * @param[in] port The port number to be checked + */ + static bool isSipPort(uint16_t port) { return port == 5060 || port == 5061; } + + protected: + SipLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : TextBasedProtocolMessage(data, dataLen, prevLayer, packet) {} + SipLayer() : TextBasedProtocolMessage() {} + SipLayer(const SipLayer& other) : TextBasedProtocolMessage(other) {} + SipLayer& operator=(const SipLayer& other) { TextBasedProtocolMessage::operator=(other); return *this; } + + // implementation of abstract methods + char getHeaderFieldNameValueSeparator() const { return ':'; } + bool spacesAllowedBetweenHeaderFieldNameAndValue() const { return true; } + }; + + + class SipRequestFirstLine; + + + /** + * @class SipRequestLayer + * Represents a SIP request header and inherits all basic functionality of SipLayer and TextBasedProtocolMessage. + * The functionality that is added for this class is the SIP first line concept. A SIP request has the following first line: + * INVITE sip:bla@bla.com:12345 SIP/2.0 + * Since it's not an "ordinary" header field, it requires a special treatment and gets a class of it's own: SipRequestFirstLine. + * In most cases a SIP request will be contained in a single packet but for cases it is not, only the first packet will be identified as SIP + * request layer. You can find out whether the header is complete by using SipLayer#isHeaderComplete() + */ + class SipRequestLayer : public SipLayer + { + friend class SipRequestFirstLine; + + public: + /** + * SIP request methods + */ + enum SipMethod + { + /** INVITE */ + SipINVITE, + /** ACK */ + SipACK, + /** BYE */ + SipBYE, + /** CANCEL */ + SipCANCEL, + /** REFISTER */ + SipREGISTER, + /** PRACK */ + SipPRACK, + /** OPTIONS */ + SipOPTIONS, + /** SUBSCRIBE */ + SipSUBSCRIBE, + /** NOTIFY */ + SipNOTIFY, + /** PUBLISH */ + SipPUBLISH, + /** INFO */ + SipINFO, + /** REFER */ + SipREFER, + /** MESSAGE */ + SipMESSAGE, + /** UPDATE */ + SipUPDATE, + /** Unknown SIP method */ + SipMethodUnknown + }; + + /** A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + SipRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * A constructor that allocates a new SIP request with only the first line filled. The request will be created without further fields. + * The user can then add fields using addField() or insertField() methods + * @param[in] method The SIP method to be used in this SIP request + * @param[in] requestUri The URI of the request + * @param[in] version SIP version to be used in this request. Default is "SIP/2.0" + */ + SipRequestLayer(SipMethod method, const std::string& requestUri, const std::string& version = "SIP/2.0"); + + ~SipRequestLayer(); + + /** + * A copy constructor for this layer. Inherits base copy constructor SipLayer and adds the functionality + * of copying the first line + * @param[in] other The instance to copy from + */ + SipRequestLayer(const SipRequestLayer& other); + + /** + * An assignment operator overload for this layer. This method inherits base assignment operator SipLayer#operator=() and adds the functionality + * of copying the first line + * @param[in] other The instance to copy from + */ + SipRequestLayer& operator=(const SipRequestLayer& other); + + /** + * @return A pointer to the first line instance for this message + */ + SipRequestFirstLine* getFirstLine() const { return m_FirstLine; } + + // implement Layer's abstract methods + + std::string toString() const; + + private: + SipRequestFirstLine* m_FirstLine; + }; + + + + + class SipResponseFirstLine; + + + /** + * @class SipResponseLayer + * Represents an SIP response message and inherits all basic functionality of SipLayer and TextBasedProtocolMessage. + * The functionality that is added for this class is the SIP first line concept. A SIP response has the following first line: + * 200 OK SIP/2.0 + * Since it's not an "ordinary" header field, it requires a special treatment and gets a class of it's own: SipResponseFirstLine. + * In most cases a SIP response will be contained in a single packet but for cases it is not, only the first packet will be identified as SIP + * response layer. You can find out whether the header is complete by using SipLayer#isHeaderComplete() + */ + class SipResponseLayer : public SipLayer + { + friend class SipResponseFirstLine; + public: + + /** + * Enum for SIP response status codes. List is taken from Wikipedia: https://en.wikipedia.org/wiki/List_of_SIP_response_codes + */ + enum SipResponseStatusCode + { + /** Extended search being performed may take a significant time so a forking proxy must send a 100 Trying response */ + Sip100Trying, + /** Destination user agent received INVITE, and is alerting user of call */ + Sip180Ringing, + /** Servers can optionally send this response to indicate a call is being forwarded */ + Sip181CallisBeingForwarded, + /** Indicates that the destination was temporarily unavailable, so the server has queued the call until the destination is available. A server may send multiple 182 responses to update progress of the queue */ + Sip182Queued, + /** This response may be used to send extra information for a call which is still being set up */ + Sip183SessioninProgress, + /** Can be used by User Agent Server to indicate to upstream SIP entities (including the User Agent Client (UAC)) that an early dialog has been terminated */ + Sip199EarlyDialogTerminated, + /** Indicates the request was successful */ + Sip200OK, + /** Indicates that the request has been accepted for processing, but the processing has not been completed */ + Sip202Accepted, + /** Indicates the request was successful, but the corresponding response will not be received */ + Sip204NoNotification, + /** The address resolved to one of several options for the user or client to choose between, which are listed in the message body or the message's Contact fields */ + Sip300MultipleChoices, + /** The original Request-URI is no longer valid, the new address is given in the Contact header field, and the client should update any records of the original Request-URI with the new value */ + Sip301MovedPermanently, + /** The client should try at the address in the Contact field. If an Expires field is present, the client may cache the result for that period of time */ + Sip302MovedTemporarily, + /** The Contact field details a proxy that must be used to access the requested destination */ + Sip305UseProxy, + /** The call failed, but alternatives are detailed in the message body */ + Sip380AlternativeService, + /** The request could not be understood due to malformed syntax */ + Sip400BadRequest, + /** The request requires user authentication. This response is issued by UASs and registrars */ + Sip401Unauthorized, + /** Reserved for future use */ + Sip402PaymentRequired, + /** The server understood the request, but is refusing to fulfill it */ + Sip403Forbidden, + /** The server has definitive information that the user does not exist at the domain specified in the Request-URI. This status is also returned if the domain in the Request-URI does not match any of the domains handled by the recipient of the request */ + Sip404NotFound, + /** The method specified in the Request-Line is understood, but not allowed for the address identified by the Request-URI */ + Sip405MethodNotAllowed, + /** The resource identified by the request is only capable of generating response entities that have content characteristics but not acceptable according to the Accept header field sent in the request */ + Sip406NotAcceptable, + /** The request requires user authentication. This response is issued by proxys */ + Sip407ProxyAuthenticationRequired, + /** Couldn't find the user in time. The server could not produce a response within a suitable amount of time, for example, if it could not determine the location of the user in time. The client MAY repeat the request without modifications at any later time */ + Sip408RequestTimeout, + /** User already registered */ + Sip409Conflict, + /** The user existed once, but is not available here any more */ + Sip410Gone, + /** The server will not accept the request without a valid Content-Length */ + Sip411LengthRequired, + /** The given precondition has not been met */ + Sip412ConditionalRequestFailed, + /** Request body too large */ + Sip413RequestEntityTooLarge, + /** The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret */ + Sip414RequestURITooLong, + /** Request body in a format not supported */ + Sip415UnsupportedMediaType, + /** Request-URI is unknown to the server */ + Sip416UnsupportedURIScheme, + /** There was a resource-priority option tag, but no Resource-Priority header */ + Sip417UnknownResourcePriority, + /** Bad SIP Protocol Extension used, not understood by the server */ + Sip420BadExtension, + /** The server needs a specific extension not listed in the Supported header */ + Sip421ExtensionRequired, + /** The received request contains a Session-Expires header field with a duration below the minimum timer */ + Sip422SessionIntervalTooSmall, + /** Expiration time of the resource is too short */ + Sip423IntervalTooBrief, + /** The request's location content was malformed or otherwise unsatisfactory */ + Sip424BadLocationInformation, + /** The server policy requires an Identity header, and one has not been provided */ + Sip428UseIdentityHeader, + /** The server did not receive a valid Referred-By token on the request */ + Sip429ProvideReferrerIdentity, + /** A specific flow to a user agent has failed, although other flows may succeed. This response is intended for use between proxy devices, and should not be seen by an endpoint (and if it is seen by one, should be treated as a 400 Bad Request response) */ + Sip430FlowFailed, + /** The request has been rejected because it was anonymous */ + Sip433AnonymityDisallowed, + /** The request has an Identity-Info header, and the URI scheme in that header cannot be dereferenced */ + Sip436BadIdentityInfo, + /** The server was unable to validate a certificate for the domain that signed the request */ + Sip437UnsupportedCertificate, + /** The server obtained a valid certificate that the request claimed was used to sign the request, but was unable to verify that signature */ + Sip438InvalidIdentityHeader, + /** The first outbound proxy the user is attempting to register through does not support the "outbound" feature of RFC 5626, although the registrar does */ + Sip439FirstHopLacksOutboundSupport, + /** If a SIP proxy determines a response context has insufficient Incoming Max-Breadth to carry out a desired parallel fork, and the proxy is unwilling/unable to compensate by forking serially or sending a redirect, that proxy MUST return a 440 response. A client receiving a 440 response can infer that its request did not reach all possible destinations */ + Sip440MaxBreadthExceeded, + /** If a SIP UA receives an INFO request associated with an Info Package that the UA has not indicated willingness to receive, the UA MUST send a 469 response, which contains a Recv-Info header field with Info Packages for which the UA is willing to receive INFO requests */ + Sip469BadInfoPackage, + /** The source of the request did not have the permission of the recipient to make such a request */ + Sip470ConsentNeeded, + /** Callee currently unavailable */ + Sip480TemporarilyUnavailable, + /** Server received a request that does not match any dialog or transaction */ + Sip481Call_TransactionDoesNotExist, + /** Server has detected a loop */ + Sip482LoopDetected, + /** Max-Forwards header has reached the value '0' */ + Sip483TooManyHops, + /** Request-URI incomplete */ + Sip484AddressIncomplete, + /** Request-URI is ambiguous */ + Sip485Ambiguous, + /** Callee is busy */ + Sip486BusyHere, + /** Request has terminated by bye or cancel */ + Sip487RequestTerminated, + /** Some aspect of the session description or the Request-URI is not acceptable */ + Sip488NotAcceptableHere, + /** The server did not understand an event package specified in an Event header field */ + Sip489BadEvent, + /** Server has some pending request from the same dialog */ + Sip491RequestPending, + /** Request contains an encrypted MIME body, which recipient can not decrypt */ + Sip493Undecipherable, + /** The server has received a request that requires a negotiated security mechanism, and the response contains a list of suitable security mechanisms for the requester to choose between, or a digest authentication challenge */ + Sip494SecurityAgreementRequired, + /** The server could not fulfill the request due to some unexpected condition */ + Sip500ServerInternalError, + /** The server does not have the ability to fulfill the request, such as because it does not recognize the request method. (Compare with 405 Method Not Allowed, where the server recognizes the method but does not allow or support it.) */ + Sip501NotImplemented, + /** The server is acting as a gateway or proxy, and received an invalid response from a downstream server while attempting to fulfill the request */ + Sip502BadGateway, + /** The server is undergoing maintenance or is temporarily overloaded and so cannot process the request. A "Retry-After" header field may specify when the client may reattempt its request */ + Sip503ServiceUnavailable, + /** The server attempted to access another server in attempting to process the request, and did not receive a prompt response */ + Sip504ServerTimeout, + /** The SIP protocol version in the request is not supported by the server */ + Sip505VersionNotSupported, + /** The request message length is longer than the server can process */ + Sip513MessageTooLarge, + /** The server is unable or unwilling to meet some constraints specified in the offer */ + Sip580PreconditionFailure, + /** All possible destinations are busy. Unlike the 486 response, this response indicates the destination knows there are no alternative destinations (such as a voicemail server) able to accept the call */ + Sip600BusyEverywhere, + /** The destination does not wish to participate in the call, or cannot do so, and additionally the destination knows there are no alternative destinations (such as a voicemail server) willing to accept the call */ + Sip603Decline, + /** The server has authoritative information that the requested user does not exist anywhere */ + Sip604DoesNotExistAnywhere, + /** The user's agent was contacted successfully but some aspects of the session description such as the requested media, bandwidth, or addressing style were not acceptable */ + Sip606NotAcceptable, + /** The called party did not want this call from the calling party. Future attempts from the calling party are likely to be similarly rejected */ + Sip607Unwanted, + /** Unknown SIP status code */ + SipStatusCodeUnknown + }; + + /** A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + SipResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * A constructor that allocates a new SIP response with only the first line filled. The request will be created without further fields. + * The user can then add fields using addField() or insertField() methods + * @param[in] statusCode SIP status code to set + * @param[in] statusCodeString Most status codes have their default string, e.g 200 is usually "OK" etc. + * But the user can set a non-default status code string and it will be written in the header first line. Empty string ("") means using the + * default status code string. Also, the default is using the default status code string + * @param[in] sipVersion SIP version to set, default is SIP/2.0 + * + */ + explicit SipResponseLayer(SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString = "", const std::string& sipVersion = "SIP/2.0"); + + virtual ~SipResponseLayer(); + + /** + * A copy constructor for this layer. This copy constructor inherits base copy constructor SipLayer and adds the functionality + * of copying the first line as well + * @param[in] other The instance to copy from + */ + SipResponseLayer(const SipResponseLayer& other); + + /** + * An assignment operator overload for this layer. This method inherits base assignment operator SipLayer#operator=() and adds the functionality + * of copying the first line as well + * @param[in] other The instance to copy from + */ + SipResponseLayer& operator=(const SipResponseLayer& other); + + /** + * @return A pointer to the first line instance for this message + */ + SipResponseFirstLine* getFirstLine() const { return m_FirstLine; } + + // implement Layer's abstract methods + + std::string toString() const; + + private: + SipResponseFirstLine* m_FirstLine; + }; + + + + /** + * @class SipRequestFirstLine + * Represents an SIP request first line. The first line includes 3 parameters: SIP method (e.g INVITE, ACK, BYE, etc.), + * URI (e.g sip:bla@bla.com:12345) and SIP version (usually SIP/2.0). All these parameters are included in this class, and the user + * can retrieve or set them. + * This class cannot be instantiated by users, it's created inside SipRequestLayer and user can get a pointer to an instance of it. All "getters" + * of this class retrieve the actual data of the SIP request and the "setters" actually change the packet data. + * Since SIP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So many "setter" methods + * of this class may need to shorten or extend the data in SipRequestLayer. These methods will return a false value if this action failed + */ + class SipRequestFirstLine + { + friend class SipRequestLayer; + public: + + /** + * @return The SIP request method + */ + SipRequestLayer::SipMethod getMethod() const { return m_Method; } + + /** + * Set the SIP request method + * @param[in] newMethod The method to set + * @return False if newMethod is SipRequestLayer#SipMethodUnknown or if shortening/extending the SipRequestLayer data failed. True otherwise + */ + bool setMethod(SipRequestLayer::SipMethod newMethod); + + /** + * @return A copied version of the URI (notice changing the return value won't change the actual data of the packet) + */ + std::string getUri() const; + + /** + * Set the URI + * @param[in] newUri The URI to set + * @return False if shortening/extending the SipRequestLayer data failed. True otherwise + */ + bool setUri(const std::string& newUri); + + /** + * @return The SIP version + */ + std::string getVersion() const { return m_Version; } + + /** + * A static method for parsing the SIP method out of raw data + * @param[in] data The raw data + * @param[in] dataLen The raw data length + * @return The parsed SIP method + */ + static SipRequestLayer::SipMethod parseMethod(char* data, size_t dataLen); + + /** + * @return The size in bytes of the SIP request first line + */ + int getSize() const { return m_FirstLineEndOffset; } + + /** + * As explained in SipRequestLayer, a SIP message can sometimes spread over more than 1 packet, so when looking at a single packet + * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication + * whether the first line is partial + * @return False if the first line is partial, true if it's complete + */ + bool isComplete() const { return m_IsComplete; } + + /** + * @class SipRequestFirstLineException + * This exception can be thrown while constructing SipRequestFirstLine (the constructor is private, so the construction happens + * only in SipRequestLayer). This kind of exception is thrown if trying to construct with SIP method of + * SipRequestLayer#SipMethodUnknown or with empty SIP version + */ + class SipRequestFirstLineException : public std::exception + { + public: + ~SipRequestFirstLineException() throw() {} + void setMessage(const std::string &message) { m_Message = message; } + virtual const char* what() const throw() + { + return m_Message.c_str(); + } + private: + std::string m_Message; + }; + + private: + SipRequestFirstLine(SipRequestLayer* sipRequest); + SipRequestFirstLine(SipRequestLayer* sipRequest, SipRequestLayer::SipMethod method, const std::string& version, const std::string& uri); + //throw(SipRequestFirstLineException); // Deprecated in C++17 + + void parseVersion(); + + SipRequestLayer* m_SipRequest; + SipRequestLayer::SipMethod m_Method; + std::string m_Version; + int m_VersionOffset; + int m_UriOffset; + int m_FirstLineEndOffset; + bool m_IsComplete; + SipRequestFirstLineException m_Exception; + }; + + + + + /** + * @class SipResponseFirstLine + * Represents an SIP response message first line. The first line includes 2 parameters: status code (e.g 100 Trying ,200 OK, etc.), + * and SIP version (usually SIP/2.0). These 2 parameters are included in this class, and the user can retrieve or set them. + * This class cannot be instantiated by users, it's created inside SipResponseLayer and user can get a pointer to an instance of it. The "getter" + * methods of this class will retrieve the actual data of the SIP response and the "setter" methods will change the packet data. + * Since SIP is a textual protocol, most fields aren't of fixed size and this also applies to the first line parameters. So most "setter" methods + * of this class may need to shorten or extend the data in SipResponseLayer. These methods will return a false value if this action failed + */ + class SipResponseFirstLine + { + friend class SipResponseLayer; + public: + /** + * @return The status code as SipResponseLayer#SipResponseStatusCode enum + */ + SipResponseLayer::SipResponseStatusCode getStatusCode() const { return m_StatusCode; } + + /** + * @return The status code number as integer (e.g 200, 100, etc.) + */ + int getStatusCodeAsInt() const; + + /** + * @return The status code message (e.g "OK", "Trying", etc.) + */ + std::string getStatusCodeString() const; + + /** + * Set the status code + * @param[in] newStatusCode The new status code to set + * @param[in] statusCodeString An optional parameter: set a non-default status code message (e.g "Bla Bla" instead of "Not Found"). If + * this parameter isn't supplied or supplied as empty string (""), the default message for the status code will be set + */ + bool setStatusCode(SipResponseLayer::SipResponseStatusCode newStatusCode, std::string statusCodeString = ""); + + /** + * @return The SIP version + */ + std::string getVersion() const { return m_Version; } + + /** + * Set the SIP version. The version to set is expected to be in the format of SIP/x.y otherwise an error will be written to log + * @param[in] newVersion The SIP version to set + */ + void setVersion(const std::string& newVersion); + + /** + * A static method for parsing the SIP status code out of raw data + * @param[in] data The raw data + * @param[in] dataLen The raw data length + * @return The parsed SIP status code as enum + */ + static SipResponseLayer::SipResponseStatusCode parseStatusCode(char* data, size_t dataLen); + + /** + * A static method for parsing the SIP version out of raw data + * @param[in] data The raw data + * @param[in] dataLen The raw data length + * @return The parsed SIP version string or an empty string if version cannot be extracted + */ + static std::string parseVersion(char* data, size_t dataLen); + + /** + * @return The size in bytes of the SIP response first line + */ + int getSize() const { return m_FirstLineEndOffset; } + + /** + * As explained in SipResponseLayer, A SIP message can sometimes spread over more than 1 packet, so when looking at a single packet + * the header can be partial. Same goes for the first line - it can spread over more than 1 packet. This method returns an indication + * whether the first line is partial + * @return False if the first line is partial, true if it's complete + */ + bool isComplete() const { return m_IsComplete; } + + /** + * @class SipResponseFirstLineException + * This exception can be thrown while constructing SipResponseFirstLine (the constructor is private, so the construction happens + * only in SipResponseLayer). This kind of exception will be thrown if trying to construct with SIP status code of + * SipResponseLayer#SipStatusCodeUnknown or with an empty SIP version + */ + class SipResponseFirstLineException : public std::exception + { + public: + ~SipResponseFirstLineException() throw() {} + void setMessage(const std::string &message) { m_Message = message; } + virtual const char* what() const throw() + { + return m_Message.c_str(); + } + private: + std::string m_Message; + }; + + private: + SipResponseFirstLine(SipResponseLayer* sipResponse); + SipResponseFirstLine(SipResponseLayer* sipResponse, const std::string& version, SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString = ""); + + static SipResponseLayer::SipResponseStatusCode validateStatusCode(char* data, size_t dataLen, SipResponseLayer::SipResponseStatusCode potentialCode); + + + SipResponseLayer* m_SipResponse; + std::string m_Version; + SipResponseLayer::SipResponseStatusCode m_StatusCode; + int m_FirstLineEndOffset; + bool m_IsComplete; + SipResponseFirstLineException m_Exception; + }; + +} + +#endif // PACKETPP_SIP_LAYER diff --git a/Packet++/header/SllLayer.h b/Packet++/header/SllLayer.h index 9525c55e9f..4815d80911 100644 --- a/Packet++/header/SllLayer.h +++ b/Packet++/header/SllLayer.h @@ -1,110 +1,110 @@ -#ifndef PACKETPP_SLL_LAYER -#define PACKETPP_SLL_LAYER - -#include "MacAddress.h" -#include "Layer.h" - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - - /** - * @struct sll_header - * Represents SLL header - */ -#pragma pack(push, 1) - struct sll_header - { - /** Specifies whether packet was: specifically sent to us by somebody else (value=0); - * broadcast by somebody else (value=1); multicast, but not broadcast, by somebody else (value=2); - * sent to somebody else by somebody else (value=3); sent by us (value=4) - **/ - uint16_t packet_type; - /** Contains a Linux ARPHRD_ value for the link-layer device type */ - uint16_t ARPHRD_type; - /** Contains the length of the link-layer address of the sender of the packet. That length could be zero */ - uint16_t link_layer_addr_len; - /** contains the link-layer address of the sender of the packet; the number of bytes of that field that are - * meaningful is specified by the link-layer address length field - **/ - uint8_t link_layer_addr[8]; - /** Contains an Ethernet protocol type of the next layer */ - uint16_t protocol_type; - }; -#pragma pack(pop) - - /** - * @class SllLayer - * Represents an SLL (Linux cooked capture) protocol layer - */ - class SllLayer : public Layer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to ether_header) - * @param[in] dataLen Size of the data in bytes - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - SllLayer(uint8_t* data, size_t dataLen, Packet* packet) : Layer(data, dataLen, NULL, packet) { m_Protocol = SLL; } - - /** - * A constructor that creates a new SLL header and allocates the data - * @param[in] packetType The packet type - * @param[in] ARPHRDType The ARPHRD type - */ - SllLayer(uint16_t packetType, uint16_t ARPHRDType); - - ~SllLayer() {} - - /** - * Get a pointer to the Sll header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the sll_header - */ - sll_header* getSllHeader() const { return (sll_header*)m_Data; } - - /** - * A setter for the link layer address field - * @param[in] addr The address to set. Memory will be copied to packet - * @param[in] addrLength Address length, must be lower or equal to 8 (which is max length for SLL address) - * @return True if address was set successfully, or false of addrLength is out of bounds (0 or larger than 8) - */ - bool setLinkLayerAddr(uint8_t* addr, size_t addrLength); - - /** - * Set a MAC address in the link layer address field - * @param[in] macAddr MAC address to set - * @return True if address was set successfully, false if MAC address isn't valid or if set failed - */ - bool setMacAddressAsLinkLayer(MacAddress macAddr); - - /** - * Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, PPPoESessionLayer, PPPoEDiscoveryLayer, - * MplsLayer. - * Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return Size of sll_header - */ - size_t getHeaderLen() const { return sizeof(sll_header); } - - /** - * Calculate the next protocol type for known protocols: IPv4, IPv6, ARP, VLAN - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } - }; - -} // namespace pcpp - -#endif /* PACKETPP_SLL_LAYER */ +#ifndef PACKETPP_SLL_LAYER +#define PACKETPP_SLL_LAYER + +#include "MacAddress.h" +#include "Layer.h" + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + + /** + * @struct sll_header + * Represents SLL header + */ +#pragma pack(push, 1) + struct sll_header + { + /** Specifies whether packet was: specifically sent to us by somebody else (value=0); + * broadcast by somebody else (value=1); multicast, but not broadcast, by somebody else (value=2); + * sent to somebody else by somebody else (value=3); sent by us (value=4) + **/ + uint16_t packet_type; + /** Contains a Linux ARPHRD_ value for the link-layer device type */ + uint16_t ARPHRD_type; + /** Contains the length of the link-layer address of the sender of the packet. That length could be zero */ + uint16_t link_layer_addr_len; + /** contains the link-layer address of the sender of the packet; the number of bytes of that field that are + * meaningful is specified by the link-layer address length field + **/ + uint8_t link_layer_addr[8]; + /** Contains an Ethernet protocol type of the next layer */ + uint16_t protocol_type; + }; +#pragma pack(pop) + + /** + * @class SllLayer + * Represents an SLL (Linux cooked capture) protocol layer + */ + class SllLayer : public Layer + { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to ether_header) + * @param[in] dataLen Size of the data in bytes + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + SllLayer(uint8_t* data, size_t dataLen, Packet* packet) : Layer(data, dataLen, NULL, packet) { m_Protocol = SLL; } + + /** + * A constructor that creates a new SLL header and allocates the data + * @param[in] packetType The packet type + * @param[in] ARPHRDType The ARPHRD type + */ + SllLayer(uint16_t packetType, uint16_t ARPHRDType); + + ~SllLayer() {} + + /** + * Get a pointer to the Sll header. Notice this points directly to the data, so every change will change the actual packet data + * @return A pointer to the sll_header + */ + sll_header* getSllHeader() const { return (sll_header*)m_Data; } + + /** + * A setter for the link layer address field + * @param[in] addr The address to set. Memory will be copied to packet + * @param[in] addrLength Address length, must be lower or equal to 8 (which is max length for SLL address) + * @return True if address was set successfully, or false of addrLength is out of bounds (0 or larger than 8) + */ + bool setLinkLayerAddr(uint8_t* addr, size_t addrLength); + + /** + * Set a MAC address in the link layer address field + * @param[in] macAddr MAC address to set + * @return True if address was set successfully, false if MAC address isn't valid or if set failed + */ + bool setMacAddressAsLinkLayer(MacAddress macAddr); + + /** + * Currently identifies the following next layers: IPv4Layer, IPv6Layer, ArpLayer, VlanLayer, PPPoESessionLayer, PPPoEDiscoveryLayer, + * MplsLayer. + * Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return Size of sll_header + */ + size_t getHeaderLen() const { return sizeof(sll_header); } + + /** + * Calculate the next protocol type for known protocols: IPv4, IPv6, ARP, VLAN + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelDataLinkLayer; } + }; + +} // namespace pcpp + +#endif /* PACKETPP_SLL_LAYER */ diff --git a/Packet++/header/SomeIpLayer.h b/Packet++/header/SomeIpLayer.h index ef6418fbc2..2a9b94a554 100644 --- a/Packet++/header/SomeIpLayer.h +++ b/Packet++/header/SomeIpLayer.h @@ -1,461 +1,461 @@ -#ifndef PACKETPP_SOMEIP_LAYER -#define PACKETPP_SOMEIP_LAYER - -#include "Layer.h" -#include - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - -/** - * @class SomeIpLayer - * Represents a SOME/IP protocol layer - */ -class SomeIpLayer : public Layer -{ -public: - /** - * SOME/IP message types - */ - enum class MsgType : uint8_t - { - /** A request expecting a response (even void) */ - REQUEST = 0x00, - /** Acknowledgment for REQUEST(optional) */ - REQUEST_ACK = 0x40, - /** A fire&forget request */ - REQUEST_NO_RETURN = 0x01, - /** Acknowledgment for REQUEST_NO_RETURN(informational) */ - REQUEST_NO_RETURN_ACK = 0x41, - /** A request of a notification expecting no response */ - NOTIFICATION = 0x02, - /** Acknowledgment for NOTIFICATION(informational) */ - NOTIFICATION_ACK = 0x42, - /** The response message */ - RESPONSE = 0x80, - /** The Acknowledgment for RESPONSE(informational) */ - RESPONSE_ACK = 0xC0, - /** The response containing an error */ - ERRORS = 0x81, - /** Acknowledgment for ERROR(informational) */ - ERROR_ACK = 0xC1, - /** A TP request expecting a response (even void) */ - TP_REQUEST = 0x20, - /** A TP fire&forget request */ - TP_REQUEST_NO_RETURN = 0x21, - /** A TP request of a notification/event callback expecting no response */ - TP_NOTIFICATION = 0x22, - /** The TP response message */ - TP_RESPONSE = 0xa0, - /** The TP response containing an error */ - TP_ERROR = 0xa1, - }; - - /** - * @struct someiphdr - * Represents a SOME/IP protocol header - */ -#pragma pack(push, 1) - struct someiphdr - { - /** Service ID */ - uint16_t serviceID; - /** Method ID. Most significant bit 0 when E2E communication. 1 when SOME/IP event */ - uint16_t methodID; - /** Length. Also covers payload. Excludes serviceID, methodID and length field itself */ - uint32_t length; - /** Client ID */ - uint16_t clientID; - /** Session ID */ - uint16_t sessionID; - /** Protocol Version */ - uint8_t protocolVersion; - /** Interface Version */ - uint8_t interfaceVersion; - /** Message Type */ - uint8_t msgType; - /** Return Code */ - uint8_t returnCode; - }; -#pragma pack(pop) - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to someiphdr) - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - SomeIpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : Layer(data, dataLen, prevLayer, packet) - { - m_Protocol = SomeIP; - } - - /** - * Construct a new layer object - * @param[in] serviceID Service ID - * @param[in] methodID Method ID - * @param[in] clientID Client ID - * @param[in] sessionID Session ID - * @param[in] interfaceVersion Interface Version - * @param[in] type Type of the message - * @param[in] returnCode Return Code - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * holds the reference to a data buffer. This option can be used to reduce the number of copies to generate packets. - */ - SomeIpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, uint8_t interfaceVersion, - MsgType type, uint8_t returnCode, const uint8_t *const data = nullptr, size_t dataLen = 0); - - /** - * Destroy the layer object - */ - ~SomeIpLayer() {} - - /** - * A static method that creates a SOME/IP or SOME/IP-TP layer from packet raw data. Returns PayloadLayer if data is - * not valid. - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored - * @return Layer* A newly allocated layer - */ - static Layer* parseSomeIpLayer(uint8_t *data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * Get a pointer to the basic SOME/IP header. Notice this points directly to the data, so every change will change - * the actual packet data - * @return A pointer to the someiphdr - */ - someiphdr *getSomeIpHeader() const { return (someiphdr *)m_Data; } - - /** - * Checks if given port is a SOME/IP protocol port (only Service Discovery ports are checked for now) - * @param[in] port Port to check - * @return true if SOME/IP protocol port, false if not - */ - static bool isSomeIpPort(uint16_t port); - - /** - * Adds port to a list of ports where pcap checks for SOME/IP communication. - * Each port must be removed at the end in order to have no memory leak. - * @param[in] port Port to add - */ - static void addSomeIpPort(uint16_t port); - - /** - * Removes port from a list of ports where pcap checks for SOME/IP communication. - * @param[in] port Port to remove - */ - static void removeSomeIpPort(uint16_t port); - - /** - * Removes all ports from a list of ports where pcap checks for SOME/IP communication. - */ - static void removeAllSomeIpPorts(); - - /** - * Get the messageID - * @return uint32_t returned in host endian - */ - uint32_t getMessageID() const; - - /** - * Set the Message ID - * @param[in] messageID messageID to set - */ - void setMessageID(uint32_t messageID); - - /** - * Get the serviceID - * @return uint16_t returned in host endian - */ - uint16_t getServiceID() const; - - /** - * Set the Service ID - * @param[in] serviceID serviceID to set - */ - void setServiceID(uint16_t serviceID); - - /** - * Get the methodID - * @return uint16_t returned in host endian - */ - uint16_t getMethodID() const; - - /** - * Set the Method ID - * @param[in] methodID methodID to set - */ - void setMethodID(uint16_t methodID); - - /** - * Get the Length Field of the SOME/IP header - * @return uint32_t The length field of the SOME/IP header - */ - uint32_t getLengthField() const; - - /** - * Get the requestID - * @return uint32_t returned in host endian - */ - uint32_t getRequestID() const; - - /** - * Set the Request ID - * @param[in] requestID requestID to set - */ - void setRequestID(uint32_t requestID); - - /** - * Get the sessionID - * @return uint16_t returned in host endian - */ - uint16_t getSessionID() const; - - /** - * Set the Session ID - * @param[in] sessionID sessionID to set - */ - void setSessionID(uint16_t sessionID); - - /** - * Get the clientID - * @return uint16_t returned in host endian - */ - uint16_t getClientID() const; - - /** - * Set the Client ID - * @param[in] clientID clientID to set - */ - void setClientID(uint16_t clientID); - - /** - * Get the protocolVersion - * @return uint8_t - */ - uint8_t getProtocolVersion() const; - - /** - * Set the Protocol Version - * @param[in] version version to set - */ - void setProtocolVersion(uint8_t version); - - /** - * Get the interfaceVersion - * @return uint8_t - */ - uint8_t getInterfaceVersion() const; - - /** - * Set the Interface Version - * @param[in] version version to set - */ - void setInterfaceVersion(uint8_t version); - - /** - * Get the message type - * @return uint8_t - */ - uint8_t getMessageTypeAsInt() const; - - /** - * Get the message type - * @return SomeIpLayer::MsgType - */ - SomeIpLayer::MsgType getMessageType() const; - - /** - * Set the Message Type - * @param[in] type Type to set - */ - void setMessageType(MsgType type); - - /** - * Set the Message Type - * @param[in] type Type to set - */ - void setMessageType(uint8_t type); - - /** - * Get the returnCode - * @return uint8_t - */ - uint8_t getReturnCode() const; - - /** - * Set the returnCode - * @param[in] returnCode ReturnCode to set - */ - void setReturnCode(uint8_t returnCode); - - /** - * Set the length field of the SOME/IP header - * @param[in] payloadLength Length of the payload - */ - void setPayloadLength(uint32_t payloadLength); - - /** - * @return A pointer for the layer payload, meaning the first byte after the header - */ - uint8_t *getPduPayload() const { return m_Data + getSomeIpHeaderLen(); } - - /** - * @return The size in bytes of the payload - */ - size_t getPduPayloadSize() const { return getHeaderLen() - getSomeIpHeaderLen(); } - - /** - * Get the Length of the SOME/IP header inc payload - * @return size_t - */ - size_t getHeaderLen() const { return sizeof(uint32_t) * 2 + getLengthField(); } - - /** - * Does nothing for this layer - */ - virtual void computeCalculateFields() {} - - /** - * Identifies the following next layers: SomeIpLayer, SomeIpTpLayer, SomeIpSdLayer. Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return The string representation of the SOME/IP layer - */ - virtual std::string toString() const; - - /** - * @return The OSI model layer of this layer - */ - OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } - -protected: - SomeIpLayer() {} - -private: - static const uint8_t SOMEIP_PROTOCOL_VERSION = 1; - virtual size_t getSomeIpHeaderLen() const { return sizeof(someiphdr); } - - /* Using unordered_set since insertion and search should be almost constant time */ - static std::unordered_set m_SomeIpPorts; -}; - -/** - * @class SomeIpTpLayer - * Represents an SOME/IP Transport Protocol Layer - */ -class SomeIpTpLayer : public SomeIpLayer -{ -public: - /** - * @struct someiptphdr - * Represents an SOME/IP-TP protocol header. - */ -#pragma pack(push, 1) - struct someiptphdr : someiphdr - { - /** Contains the offset and the more segments flag. 28 bit offset field measured in 16 bytes + 3 bit reserved + - * 1 bit more segments flag */ - uint32_t offsetAndFlag; - }; -#pragma pack(pop) - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref someiptphdr) - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - SomeIpTpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : SomeIpLayer(data, dataLen, prevLayer, packet) {} - - /** - * A constructor that creates empty layer and sets values - * @param[in] serviceID Service ID - * @param[in] methodID Method ID - * @param[in] clientID Client ID - * @param[in] sessionID Session ID - * @param[in] interfaceVersion Interface Version - * @param[in] type Type of the message - * @param[in] returnCode Return Code - * @param[in] offset Offset indicating the data offset in increments of 16 bytes - * @param[in] moreSegmentsFlag Flag indicating whether more SOME/IP-TP Packets will follow - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - */ - SomeIpTpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, - uint8_t interfaceVersion, MsgType type, uint8_t returnCode, uint32_t offset, bool moreSegmentsFlag, - const uint8_t *const data = nullptr, size_t dataLen = 0); - - /** - * Destroy the layer object - */ - ~SomeIpTpLayer() {} - - /** - * Get a pointer to the basic SOME/IP-TP header. Notice this points directly to the data, so every change will - * change the actual packet data - * @return A pointer to the @ref someiptphdr - */ - someiptphdr *getSomeIpTpHeader() const { return (someiptphdr *)m_Data; } - - /** - * Get the Offset. Offset is returned in multiple of 16 bytes. - * @return The offset value - */ - uint32_t getOffset() const; - - /** - * Set the Offset. Already has to be in multiples of 16 bytes. - * If 32 bytes have already been transmitted, the offset has to be set to 2. - * @param[in] offset Offset to set. Already has to be in multiples of 16 bytes. - */ - void setOffset(uint32_t offset); - - /** - * Get the More Segments Flag - * @return true if the More Segments Flag is set, false if it is not set - */ - bool getMoreSegmentsFlag() const; - - /** - * Set the More Segments Flag - * @param[in] flag True if the More Segments Flag shall be set, false for resetting - */ - void setMoreSegmentsFlag(bool flag); - - /** - * Sets the message type in this layer with enabling the TP flag - */ - void computeCalculateFields(); - - /** - * @return The string representation of the SOME/IP-TP layer - */ - std::string toString() const; - -private: - static const uint32_t SOMEIP_TP_MORE_FLAG_MASK = 0x01; - static const uint32_t SOMEIP_TP_OFFSET_MASK = 0xFFFFFFF0; - - size_t getSomeIpHeaderLen() const { return sizeof(someiptphdr); } - - static uint8_t setTpFlag(uint8_t messageType); -}; - -} // namespace pcpp -#endif /* PACKETPP_SOMEIP_LAYER */ +#ifndef PACKETPP_SOMEIP_LAYER +#define PACKETPP_SOMEIP_LAYER + +#include "Layer.h" +#include + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + +/** + * @class SomeIpLayer + * Represents a SOME/IP protocol layer + */ +class SomeIpLayer : public Layer +{ +public: + /** + * SOME/IP message types + */ + enum class MsgType : uint8_t + { + /** A request expecting a response (even void) */ + REQUEST = 0x00, + /** Acknowledgment for REQUEST(optional) */ + REQUEST_ACK = 0x40, + /** A fire&forget request */ + REQUEST_NO_RETURN = 0x01, + /** Acknowledgment for REQUEST_NO_RETURN(informational) */ + REQUEST_NO_RETURN_ACK = 0x41, + /** A request of a notification expecting no response */ + NOTIFICATION = 0x02, + /** Acknowledgment for NOTIFICATION(informational) */ + NOTIFICATION_ACK = 0x42, + /** The response message */ + RESPONSE = 0x80, + /** The Acknowledgment for RESPONSE(informational) */ + RESPONSE_ACK = 0xC0, + /** The response containing an error */ + ERRORS = 0x81, + /** Acknowledgment for ERROR(informational) */ + ERROR_ACK = 0xC1, + /** A TP request expecting a response (even void) */ + TP_REQUEST = 0x20, + /** A TP fire&forget request */ + TP_REQUEST_NO_RETURN = 0x21, + /** A TP request of a notification/event callback expecting no response */ + TP_NOTIFICATION = 0x22, + /** The TP response message */ + TP_RESPONSE = 0xa0, + /** The TP response containing an error */ + TP_ERROR = 0xa1, + }; + + /** + * @struct someiphdr + * Represents a SOME/IP protocol header + */ +#pragma pack(push, 1) + struct someiphdr + { + /** Service ID */ + uint16_t serviceID; + /** Method ID. Most significant bit 0 when E2E communication. 1 when SOME/IP event */ + uint16_t methodID; + /** Length. Also covers payload. Excludes serviceID, methodID and length field itself */ + uint32_t length; + /** Client ID */ + uint16_t clientID; + /** Session ID */ + uint16_t sessionID; + /** Protocol Version */ + uint8_t protocolVersion; + /** Interface Version */ + uint8_t interfaceVersion; + /** Message Type */ + uint8_t msgType; + /** Return Code */ + uint8_t returnCode; + }; +#pragma pack(pop) + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to someiphdr) + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + SomeIpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) + : Layer(data, dataLen, prevLayer, packet) + { + m_Protocol = SomeIP; + } + + /** + * Construct a new layer object + * @param[in] serviceID Service ID + * @param[in] methodID Method ID + * @param[in] clientID Client ID + * @param[in] sessionID Session ID + * @param[in] interfaceVersion Interface Version + * @param[in] type Type of the message + * @param[in] returnCode Return Code + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * holds the reference to a data buffer. This option can be used to reduce the number of copies to generate packets. + */ + SomeIpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, uint8_t interfaceVersion, + MsgType type, uint8_t returnCode, const uint8_t *const data = nullptr, size_t dataLen = 0); + + /** + * Destroy the layer object + */ + ~SomeIpLayer() {} + + /** + * A static method that creates a SOME/IP or SOME/IP-TP layer from packet raw data. Returns PayloadLayer if data is + * not valid. + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored + * @return Layer* A newly allocated layer + */ + static Layer* parseSomeIpLayer(uint8_t *data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * Get a pointer to the basic SOME/IP header. Notice this points directly to the data, so every change will change + * the actual packet data + * @return A pointer to the someiphdr + */ + someiphdr *getSomeIpHeader() const { return (someiphdr *)m_Data; } + + /** + * Checks if given port is a SOME/IP protocol port (only Service Discovery ports are checked for now) + * @param[in] port Port to check + * @return true if SOME/IP protocol port, false if not + */ + static bool isSomeIpPort(uint16_t port); + + /** + * Adds port to a list of ports where pcap checks for SOME/IP communication. + * Each port must be removed at the end in order to have no memory leak. + * @param[in] port Port to add + */ + static void addSomeIpPort(uint16_t port); + + /** + * Removes port from a list of ports where pcap checks for SOME/IP communication. + * @param[in] port Port to remove + */ + static void removeSomeIpPort(uint16_t port); + + /** + * Removes all ports from a list of ports where pcap checks for SOME/IP communication. + */ + static void removeAllSomeIpPorts(); + + /** + * Get the messageID + * @return uint32_t returned in host endian + */ + uint32_t getMessageID() const; + + /** + * Set the Message ID + * @param[in] messageID messageID to set + */ + void setMessageID(uint32_t messageID); + + /** + * Get the serviceID + * @return uint16_t returned in host endian + */ + uint16_t getServiceID() const; + + /** + * Set the Service ID + * @param[in] serviceID serviceID to set + */ + void setServiceID(uint16_t serviceID); + + /** + * Get the methodID + * @return uint16_t returned in host endian + */ + uint16_t getMethodID() const; + + /** + * Set the Method ID + * @param[in] methodID methodID to set + */ + void setMethodID(uint16_t methodID); + + /** + * Get the Length Field of the SOME/IP header + * @return uint32_t The length field of the SOME/IP header + */ + uint32_t getLengthField() const; + + /** + * Get the requestID + * @return uint32_t returned in host endian + */ + uint32_t getRequestID() const; + + /** + * Set the Request ID + * @param[in] requestID requestID to set + */ + void setRequestID(uint32_t requestID); + + /** + * Get the sessionID + * @return uint16_t returned in host endian + */ + uint16_t getSessionID() const; + + /** + * Set the Session ID + * @param[in] sessionID sessionID to set + */ + void setSessionID(uint16_t sessionID); + + /** + * Get the clientID + * @return uint16_t returned in host endian + */ + uint16_t getClientID() const; + + /** + * Set the Client ID + * @param[in] clientID clientID to set + */ + void setClientID(uint16_t clientID); + + /** + * Get the protocolVersion + * @return uint8_t + */ + uint8_t getProtocolVersion() const; + + /** + * Set the Protocol Version + * @param[in] version version to set + */ + void setProtocolVersion(uint8_t version); + + /** + * Get the interfaceVersion + * @return uint8_t + */ + uint8_t getInterfaceVersion() const; + + /** + * Set the Interface Version + * @param[in] version version to set + */ + void setInterfaceVersion(uint8_t version); + + /** + * Get the message type + * @return uint8_t + */ + uint8_t getMessageTypeAsInt() const; + + /** + * Get the message type + * @return SomeIpLayer::MsgType + */ + SomeIpLayer::MsgType getMessageType() const; + + /** + * Set the Message Type + * @param[in] type Type to set + */ + void setMessageType(MsgType type); + + /** + * Set the Message Type + * @param[in] type Type to set + */ + void setMessageType(uint8_t type); + + /** + * Get the returnCode + * @return uint8_t + */ + uint8_t getReturnCode() const; + + /** + * Set the returnCode + * @param[in] returnCode ReturnCode to set + */ + void setReturnCode(uint8_t returnCode); + + /** + * Set the length field of the SOME/IP header + * @param[in] payloadLength Length of the payload + */ + void setPayloadLength(uint32_t payloadLength); + + /** + * @return A pointer for the layer payload, meaning the first byte after the header + */ + uint8_t *getPduPayload() const { return m_Data + getSomeIpHeaderLen(); } + + /** + * @return The size in bytes of the payload + */ + size_t getPduPayloadSize() const { return getHeaderLen() - getSomeIpHeaderLen(); } + + /** + * Get the Length of the SOME/IP header inc payload + * @return size_t + */ + size_t getHeaderLen() const { return sizeof(uint32_t) * 2 + getLengthField(); } + + /** + * Does nothing for this layer + */ + virtual void computeCalculateFields() {} + + /** + * Identifies the following next layers: SomeIpLayer, SomeIpTpLayer, SomeIpSdLayer. Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return The string representation of the SOME/IP layer + */ + virtual std::string toString() const; + + /** + * @return The OSI model layer of this layer + */ + OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } + +protected: + SomeIpLayer() {} + +private: + static const uint8_t SOMEIP_PROTOCOL_VERSION = 1; + virtual size_t getSomeIpHeaderLen() const { return sizeof(someiphdr); } + + /* Using unordered_set since insertion and search should be almost constant time */ + static std::unordered_set m_SomeIpPorts; +}; + +/** + * @class SomeIpTpLayer + * Represents an SOME/IP Transport Protocol Layer + */ +class SomeIpTpLayer : public SomeIpLayer +{ +public: + /** + * @struct someiptphdr + * Represents an SOME/IP-TP protocol header. + */ +#pragma pack(push, 1) + struct someiptphdr : someiphdr + { + /** Contains the offset and the more segments flag. 28 bit offset field measured in 16 bytes + 3 bit reserved + + * 1 bit more segments flag */ + uint32_t offsetAndFlag; + }; +#pragma pack(pop) + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref someiptphdr) + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + SomeIpTpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) + : SomeIpLayer(data, dataLen, prevLayer, packet) {} + + /** + * A constructor that creates empty layer and sets values + * @param[in] serviceID Service ID + * @param[in] methodID Method ID + * @param[in] clientID Client ID + * @param[in] sessionID Session ID + * @param[in] interfaceVersion Interface Version + * @param[in] type Type of the message + * @param[in] returnCode Return Code + * @param[in] offset Offset indicating the data offset in increments of 16 bytes + * @param[in] moreSegmentsFlag Flag indicating whether more SOME/IP-TP Packets will follow + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + */ + SomeIpTpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, + uint8_t interfaceVersion, MsgType type, uint8_t returnCode, uint32_t offset, bool moreSegmentsFlag, + const uint8_t *const data = nullptr, size_t dataLen = 0); + + /** + * Destroy the layer object + */ + ~SomeIpTpLayer() {} + + /** + * Get a pointer to the basic SOME/IP-TP header. Notice this points directly to the data, so every change will + * change the actual packet data + * @return A pointer to the @ref someiptphdr + */ + someiptphdr *getSomeIpTpHeader() const { return (someiptphdr *)m_Data; } + + /** + * Get the Offset. Offset is returned in multiple of 16 bytes. + * @return The offset value + */ + uint32_t getOffset() const; + + /** + * Set the Offset. Already has to be in multiples of 16 bytes. + * If 32 bytes have already been transmitted, the offset has to be set to 2. + * @param[in] offset Offset to set. Already has to be in multiples of 16 bytes. + */ + void setOffset(uint32_t offset); + + /** + * Get the More Segments Flag + * @return true if the More Segments Flag is set, false if it is not set + */ + bool getMoreSegmentsFlag() const; + + /** + * Set the More Segments Flag + * @param[in] flag True if the More Segments Flag shall be set, false for resetting + */ + void setMoreSegmentsFlag(bool flag); + + /** + * Sets the message type in this layer with enabling the TP flag + */ + void computeCalculateFields(); + + /** + * @return The string representation of the SOME/IP-TP layer + */ + std::string toString() const; + +private: + static const uint32_t SOMEIP_TP_MORE_FLAG_MASK = 0x01; + static const uint32_t SOMEIP_TP_OFFSET_MASK = 0xFFFFFFF0; + + size_t getSomeIpHeaderLen() const { return sizeof(someiptphdr); } + + static uint8_t setTpFlag(uint8_t messageType); +}; + +} // namespace pcpp +#endif /* PACKETPP_SOMEIP_LAYER */ diff --git a/Packet++/header/SomeIpSdLayer.h b/Packet++/header/SomeIpSdLayer.h index 3147fbd2de..2cf24162f1 100644 --- a/Packet++/header/SomeIpSdLayer.h +++ b/Packet++/header/SomeIpSdLayer.h @@ -1,769 +1,769 @@ -#ifndef PACKETPP_SOMEIPSD_LAYER -#define PACKETPP_SOMEIPSD_LAYER - -#include "EndianPortable.h" -#include "IpAddress.h" -#include "Layer.h" -#include "SomeIpLayer.h" -#include -#include -#include -#include -#include -#include - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ -/** - * Types of protocols that can be referenced in SOME/IP-SD - */ -enum SomeIpSdProtocolType : uint8_t -{ - /** TCP */ - SD_TCP = 0x06, - /** UDP */ - SD_UDP = 0x11 -}; - -class SomeIpSdLayer; - -/** - * @class SomeIpSdOption - * Base class of the SOME/IP-SD options. Cannot be instantiated. - */ -class SomeIpSdOption -{ -public: - friend class SomeIpSdLayer; - - /** - * Types of options currently available for the SOME/IP-SD protocol - */ - enum class OptionType : uint8_t - { - /** Unknown Option Type */ - Unknown = 0x00, - /** Configuration Option */ - ConfigurationString = 0x01, - /** Load Balancing Option */ - LoadBalancing = 0x02, - /** IPv4 Endpoint Option */ - IPv4Endpoint = 0x04, - /** IPv6 Endpoint Option */ - IPv6Endpoint = 0x06, - /** IPv4 Multicast Option */ - IPv4Multicast = 0x14, - /** IPv6 Multicast Option */ - IPv6Multicast = 0x16, - /** IPv4 SD Endpoint Option */ - IPv4SdEndpoint = 0x24, - /** IPv6 SD Endpoint Option */ - IPv6SdEndpoint = 0x26 - }; - - /** - * @struct someipsdhdroptionsbase - * Represents the common base for SOME/IP-SD header options - */ -#pragma pack(push, 1) - struct someipsdhdroptionsbase - { - /** Length - excluding the 16 bit Length field and the 8 bit type flag */ - uint16_t length; - /** Type */ - uint8_t type; - /** Reserved */ - uint8_t reserved; - }; -#pragma pack(pop) - - /** - * Destroy the SOME/IP-SD Option object and delete allocated data if it has been allocated by a constructor - */ - virtual ~SomeIpSdOption(); - - /** - * Get the Option Type - * @return OptionType - */ - OptionType getType() const; - - /** - * Get the Length of the SOME/IP-SD option - * @return size_t - */ - size_t getLength() const { return m_DataLen; } - - /** - * Get the internal data of the SOME/IP-SD Option - * @return uint8_t* - */ - uint8_t *getDataPtr() const; - - /** - * Get a pointer to the SOME/IP-SD Option base header - * @return someipsdhdroptionsbase* - */ - someipsdhdroptionsbase *getSomeIpSdOptionHeader() const; - -protected: - const IDataContainer *m_DataContainer; - size_t m_Offset; - uint8_t *m_ShadowData; - size_t m_DataLen; - - SomeIpSdOption() : m_DataContainer(nullptr), m_Offset(0), m_ShadowData(nullptr), m_DataLen(0) {} - - SomeIpSdOption(const IDataContainer *dataContainer, size_t offset) - : m_DataContainer(dataContainer), m_Offset(offset), m_ShadowData(nullptr), m_DataLen(0) {} - - void initStdFields(OptionType type); - - SomeIpSdOption(const SomeIpSdOption &) = delete; - SomeIpSdOption &operator=(const SomeIpSdOption &) = delete; -}; - -/** - * @class SomeIpSdIPv4Option - * Implements the following SOME/IP-SD Options: IPv4 Endpoint, IPv4 Multicast, IPv4 SD Endpoint - */ -class SomeIpSdIPv4Option : public SomeIpSdOption -{ -public: - friend class SomeIpSdLayer; - - /** - * Types of options which are implemented with this class - */ - enum IPv4OptionType - { - /** IPv4 Endpoint Option */ - IPv4Endpoint, - /** IPv4 Multicast Option */ - IPv4Multicast, - /** IPv4 SD Endpoint Option */ - IPv4SdEndpoint, - }; - - /** - * Construct a new SomeIpSdIPv4 Option object - * @param[in] type IPv4 Option type - * @param[in] ipAddress Ipv4 address to use - * @param[in] port Port to use - * @param[in] l4Protocol Protocol to use - */ - SomeIpSdIPv4Option(IPv4OptionType type, IPv4Address ipAddress, uint16_t port, SomeIpSdProtocolType l4Protocol); - - /** - * Construct a new SomeIpSdIPv4 Option object from already existing memory - * @param[in] dataContainer Data containing the SomeIpSdIPv4 Option object - * @param[in] offset Offset for dataContainer - */ - SomeIpSdIPv4Option(const IDataContainer *dataContainer, size_t offset); - - /** - * Get the Ip Address - * @return IPv4Address - */ - IPv4Address getIpAddress() const; - - /** - * Get the Port - * @return uint16_t - */ - uint16_t getPort() const; - - /** - * Get the Protocol - * @return SomeIpSdProtocolType - */ - SomeIpSdProtocolType getProtocol() const; - -private: - /** - * @struct someipsdhdroptionsipv4 - * Represents the IPv4 option types for the SOME/IP-SD header - */ -#pragma pack(push, 1) - struct someipsdhdroptionsipv4 : someipsdhdroptionsbase - { - /* IPv4-Address field */ - uint32_t ipv4Address; - /* Reserved */ - // cppcheck-suppress duplInheritedMember - uint8_t reserved; - /* Layer 4 Protocol field (L4-Proto) - Either UDP or TCP */ - SomeIpSdProtocolType l4Protocol; - /* Port number of UDP or TCP */ - uint16_t portNumber; - }; -#pragma pack(pop) -}; - -/** - * @class SomeIpSdIPv6Option - * Implements the following SOME/IP-SD Options: IPv6 Endpoint, IPv6 Multicast, IPv6 SD Endpoint - */ -class SomeIpSdIPv6Option : public SomeIpSdOption -{ -public: - friend class SomeIpSdLayer; - - /** - * Types of options which are implemented with this class - */ - enum IPv6OptionType - { - /** IPv6 Endpoint Option */ - IPv6Endpoint, - /** IPv6 Multicast Option */ - IPv6Multicast, - /** IPv6 SD Endpoint Option */ - IPv6SdEndpoint, - }; - - /** - * Construct a new SomeIpSdIPv6 Option object - * @param[in] type IPv6 Option type - * @param[in] ipAddress Ipv6 address to use - * @param[in] port Port to use - * @param[in] l4Protocol Protocol to use - */ - SomeIpSdIPv6Option(IPv6OptionType type, IPv6Address ipAddress, uint16_t port, SomeIpSdProtocolType l4Protocol); - - /** - * Construct a new SomeIpSdIPv6 Option object from already existing memory - * @param[in] dataContainer Data containing the SomeIpSdIPv6 Option object - * @param[in] offset Offset for dataContainer - */ - SomeIpSdIPv6Option(const IDataContainer *dataContainer, size_t offset); - - /** - * Get the Ip Address - * @return IPv6Address - */ - IPv6Address getIpAddress() const; - - /** - * Get the Port - * @return uint16_t - */ - uint16_t getPort() const; - - /** - * Get the Protocol - * @return SomeIpSdProtocolType - */ - SomeIpSdProtocolType getProtocol() const; - -private: - /** - * @struct someipsdhdroptionsipv6 - * Represents the IPv6 option types for the SOME/IP-SD header - */ -#pragma pack(push, 1) - struct someipsdhdroptionsipv6 : someipsdhdroptionsbase - { - /* IPv6-Address field */ - uint8_t ipv6Address[16]; - /* Reserved */ - // cppcheck-suppress duplInheritedMember - uint8_t reserved; - /* Layer 4 Protocol field (L4-Proto) - Either UDP or TCP */ - SomeIpSdProtocolType l4Protocol; - /* Port number of UDP or TCP */ - uint16_t portNumber; - }; -#pragma pack(pop) -}; - -/** - * @class SomeIpSdConfigurationOption - * Implements the Configuration option of SOME/IP-SD protocol - */ -class SomeIpSdConfigurationOption : public SomeIpSdOption -{ -public: - friend class SomeIpSdLayer; - - /** - * Construct a new Configuration Option object - * @param[in] configurationString the configuration string - */ - explicit SomeIpSdConfigurationOption(const std::string &configurationString); - - /** - * Construct a new Configuration Option object from already existing memory - * @param[in] dataContainer Data containing the Configuration Option object - * @param[in] offset Offset for dataContainer - */ - SomeIpSdConfigurationOption(const IDataContainer *dataContainer, size_t offset); - - /** - * Get the configuration string - * @return std::string - */ - std::string getConfigurationString() const; -}; - -/** - * @class SomeIpSdLoadBalancingOption - * Implements the Load Balancing option of SOME/IP-SD protocol - */ -class SomeIpSdLoadBalancingOption : public SomeIpSdOption -{ -public: - friend class SomeIpSdLayer; - - /** - * Construct a new Load Balancing object - * @param[in] priority Priority of this instance - * @param[in] weight Weight of this instance - */ - SomeIpSdLoadBalancingOption(uint16_t priority, uint16_t weight); - - /** - * Construct a new Option object from already existing memory - * @param[in] dataContainer Data containing the option object - * @param[in] offset Offset for dataContainer - */ - SomeIpSdLoadBalancingOption(const IDataContainer *dataContainer, size_t offset); - - /** - * Get the priority fild - * @return uint16_t - */ - uint16_t getPriority() const; - - /** - * Get the weight field - * @return uint16_t - */ - uint16_t getWeight() const; - -private: - /** - * @struct someipsdhdroptionsload - * Represents the Load Balancing option header for SOME/IP-SD - */ -#pragma pack(push, 1) - struct someipsdhdroptionsload : someipsdhdroptionsbase - { - /* Priority field */ - uint16_t priority; - /* Weight field */ - uint16_t weight; - }; -#pragma pack(pop) -}; - -/** - * @class SomeIpSdEntry - * Implementation of the SOME/IP-SD Service Entry and Eventgroup Entry Type - */ -class SomeIpSdEntry -{ -public: - friend class SomeIpSdLayer; - - /** - * Types of entries that can occur in SOME/IP-SD - */ - enum class EntryType : uint8_t - { - /** Find Service */ - FindService, - /** Offer Service */ - OfferService, - /** Stop Offer Service */ - StopOfferService, - /** Subscribe Eventgroup */ - SubscribeEventgroup, - /** Stop Subscribe Eventgroup */ - StopSubscribeEventgroup, - /** Subscribe Eventgroup Acknowledgment */ - SubscribeEventgroupAck, - /** Subscribe Eventgroup Negative Acknowledgement */ - SubscribeEventgroupNack, - /** Unknown Entry Type */ - UnknownEntryType - }; - - /** - * @struct someipsdhdrentry - * Represents the Service Entry Type and Eventgroup Entry Type - */ -#pragma pack(push, 1) - struct someipsdhdrentry - { - /** Type */ - uint8_t type; - /** Index 1st option */ - uint8_t indexFirstOption; - /** Index 2nd option */ - uint8_t indexSecondOption; -#if (BYTE_ORDER == LITTLE_ENDIAN) - uint8_t - /** Numbers of Option #2 (4bit) */ - nrOpt2 : 4, - /** Numbers of Option #1 (4bit) */ - nrOpt1 : 4; -#else - uint8_t - /** Numbers of Option #1 (4bit) */ - nrOpt1 : 4, - /** Numbers of Option #2 (4bit) */ - nrOpt2 : 4; -#endif - /** Service ID */ - uint16_t serviceID; - /** Instance ID */ - uint16_t instanceID; - /** Major Version (8 bit) + TTL (24 bit) */ - uint32_t majorVersion_ttl; - /** Minor Version (Service Entry Type) or Counter + Eventgroup ID (Eventgroup Entry Type) */ - uint32_t data; - }; -#pragma pack(pop) - - /** - * Construct a new SOME/IP-SD Service Entry Type - * @param[in] type Type to create - * @param[in] serviceID ServiceID to use - * @param[in] instanceID InstanceID to use - * @param[in] majorVersion MajorVersion to use - * @param[in] TTL TTL to use. Has to be 0 for all Stop* entry types - * @param[in] minorVersion MinorVersion to use - */ - SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, uint32_t TTL, - uint32_t minorVersion); - - /** - * Construct a new SOME/IP-SD Eventgroup Entry Type - * @param[in] type Type to create - * @param[in] serviceID ServiceID to use - * @param[in] instanceID InstanceID to use - * @param[in] majorVersion MajorVersion to use - * @param[in] TTL TTL to use. Has to be 0 for all Stop* entry types - * @param[in] counter Counter value to use - * @param[in] eventGroupID EventgroupId to use - */ - SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, uint32_t TTL, - uint8_t counter, uint16_t eventGroupID); - - /** - * Construct a new SomeIpSdEntry object from existing data - * @param[in] pSomeIpSdLayer Layer that this entry is created for - * @param[in] offset Offset for pSomeIpSdLayer - */ - SomeIpSdEntry(const SomeIpSdLayer *pSomeIpSdLayer, size_t offset); - - /** - * Destroy the SomeIpSd Entry object and delete allocated data if it has been allocated by a constructor - */ - ~SomeIpSdEntry(); - - /** - * Get the internal data of the SOME/IP-SD Entry - * @return uint8_t* - */ - uint8_t *getDataPtr() const; - - /** - * Get a pointer to the SOME/IP-SD Entry header - * @return someipsdhdrentry* - */ - someipsdhdrentry *getSomeIpSdEntryHeader() const; - - /** - * Get the Entry Type - * @return EntryType - */ - EntryType getType() const { return m_EntryType; } - - /** - * Get the Length of the SomeIpSd Entry - * @return size_t - */ - size_t getLength() const { return sizeof(someipsdhdrentry); } - - /** - * Get the number of Options of this Entry - * @return uint32_t - */ - uint32_t getNumOptions() const; - - /** - * Get the Service Id in host endianness - * @return uint16_t - */ - uint16_t getServiceId() const; - - /** - * Set the Service Id - * @param[in] serviceId - */ - void setServiceId(uint16_t serviceId); - - /** - * Get the Instance Id in host endianness - * @return uint16_t - */ - uint16_t getInstanceId() const; - - /** - * Set the Instance Id - * @param[in] instanceId - */ - void setInstanceId(uint16_t instanceId); - - /** - * Get the Major version field in host endianness - * @return uint16_t - */ - uint8_t getMajorVersion() const; - - /** - * Set the Major Version - * @param[in] majorVersion - */ - void setMajorVersion(uint8_t majorVersion); - - /** - * Get the Ttl field - * @return uint32_t - */ - uint32_t getTtl() const; - - /** - * Set the Ttl field - * @param[in] ttl - */ - void setTtl(uint32_t ttl); - - /** - * Get the minor version - * @return uint32_t - */ - uint32_t getMinorVersion() const; - - /** - * Set the minor version - * @param[in] minorVersion - */ - void setMinorVersion(uint32_t minorVersion); - - /** - * Get the counter value - * @return uint32_t - */ - uint8_t getCounter() const; - - /** - * Set the counter value - * @param[in] counter - */ - void setCounter(uint8_t counter); - - /** - * Get the eventgroup id - * @return uint32_t - */ - uint16_t getEventgroupId() const; - - /** - * Set the eventgroup id - * @param[in] eventgroupID - */ - void setEventgroupId(uint16_t eventgroupID); - -private: - /** - * These are the entry types used by SOME/IP-SD. They cannot be used for parameter passing since the values - * are not unique. - */ - enum class TypeInternal : uint8_t - { - /** Find Service */ - FindService_Internal = 0x00, - /** Offer Service / Stop Offer Service */ - OfferService_Internal = 0x01, - /** Subscribe Eventgroup & Stop Subscribe Eventgroup */ - SubscribeEventgroup_Internal = 0x06, - /** Subscribe Eventgroup Acknowledgment / Negative Acknowledgement */ - SubscribeEventgroupAck_Internal = 0x07, - }; - - EntryType m_EntryType; - const SomeIpSdLayer *m_Layer; - size_t m_Offset; - uint8_t *m_ShadowData; - - void initStdFields(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, uint32_t TTL); - - SomeIpSdEntry(const SomeIpSdEntry &) = delete; - SomeIpSdEntry &operator=(const SomeIpSdEntry &) = delete; - - static const uint32_t SOMEIPSD_HDR_ENTRY_MASK_TTL = 0x00FFFFFF; -}; - -/** - * @class SomeIpSdLayer - * Implementation of the SOME/IP-SD protocol - */ -class SomeIpSdLayer : public SomeIpLayer -{ -public: - friend class SomeIpSdEntry; - - typedef SomeIpSdEntry* EntryPtr; - typedef std::vector EntriesVec; - typedef SomeIpSdOption* OptionPtr; - typedef std::vector OptionsVec; - - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - SomeIpSdLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet); - - /** - * Construct a new SomeIpSdLayer object - * @param[in] serviceID Service ID - * @param[in] methodID Method ID - * @param[in] clientID Client ID - * @param[in] sessionID Session ID - * @param[in] interfaceVersion Interface Version - * @param[in] type Type of the message - * @param[in] returnCode Return Code - * @param[in] flags Flags that shall be used in the header - */ - SomeIpSdLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, - uint8_t interfaceVersion, MsgType type, uint8_t returnCode, uint8_t flags); - - /** - * Destroy the layer object - */ - ~SomeIpSdLayer() {} - - /** - * Checks if given port is a SOME/IP-SD protocol port - * @param[in] port Port to check - * @return true if SOME/IP-SD protocol port, false if not - */ - static bool isSomeIpSdPort(uint16_t port) { return port == 30490; } - - /** - * Get the Flags of the layer - * @return uint8_t Flags - */ - uint8_t getFlags() const; - - /** - * Set the Flags of the layer - * @param[in] flags Flags to set - */ - void setFlags(uint8_t flags); - - /** - * Get the number of entries in this layer - * @return uint32_t - */ - uint32_t getNumEntries() const; - - /** - * Get the number of options in this layer - * @return uint32_t - */ - uint32_t getNumOptions() const; - - /** - * Get the Entries from this layer - * @return EntriesVec Vector holding pointers to the options - */ - const EntriesVec getEntries() const; - - /** - * Get the Options from this layer - * @return OptionsVec Vector holding pointers to the options - */ - const OptionsVec getOptions() const; - - /** - * Get the Options from a specific Entry - * @param[in] index Index of the Entry, starting with 0. - * @return OptionsVec Vector holding pointers to the options - */ - const OptionsVec getOptionsFromEntry(uint32_t index) const; - - /** - * Adds a given entry to the layer and returns the index of the entry - * @param[in] entry Pointer to the entry that shall be added to the layer - * @return uint32_t Returns the index of the entry starting with 0 - */ - uint32_t addEntry(const SomeIpSdEntry &entry); - - /** - * Adds an option to an entry that has already been added to the layer by using addEntry(). The option - * is also added to the layer itself. If the option cannot by assigned to the entry, the option is not - * copied into the layer. - * @param[in] indexEntry Index of the entry where the option shall be added. First Entry has index 0 - * @param[in] option Pointer to the option that shall be added - * @return True if the option could be assigned to the entry and was copied into the layer, false otherwise - */ - bool addOptionTo(uint32_t indexEntry, const SomeIpSdOption &option); - - /** - * Does nothing for this layer - */ - void computeCalculateFields() {}; - - /** - * @return The string representation of the SOME/IP-SD layer - */ - std::string toString() const; - -private: - /** - * @struct someipsdhdr - * Represents an SOME/IP-SD protocol header - */ -#pragma pack(push, 1) - struct someipsdhdr : someiphdr - { - /** Flags (8 bit) */ - uint8_t flags; - /** Reserved1 field (Bits 0-7 of 24-bits reserved field) */ - uint8_t reserved1; - /** Reserved2 field (Bits 8-15 of 24-bits reserved field) */ - uint8_t reserved2; - /** Reserved3 field (Bits 16-23 of 24-bits reserved field) */ - uint8_t reserved3; - }; -#pragma pack(pop) - - uint32_t m_NumOptions; - - uint32_t countOptions(); - uint32_t findOption(const SomeIpSdOption &option); - void addOption(const SomeIpSdOption &option); - bool addOptionIndex(uint32_t indexEntry, uint32_t indexOffset); - OptionPtr parseOption(SomeIpSdOption::OptionType type, size_t offset) const; - - size_t getLenEntries() const; - size_t getLenOptions() const; - void setLenEntries(uint32_t length); - void setLenOptions(uint32_t length); -}; - -} // namespace pcpp -#endif /* PACKETPP_SOMEIPSD_LAYER */ +#ifndef PACKETPP_SOMEIPSD_LAYER +#define PACKETPP_SOMEIPSD_LAYER + +#include "EndianPortable.h" +#include "IpAddress.h" +#include "Layer.h" +#include "SomeIpLayer.h" +#include +#include +#include +#include +#include +#include + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ +/** + * Types of protocols that can be referenced in SOME/IP-SD + */ +enum SomeIpSdProtocolType : uint8_t +{ + /** TCP */ + SD_TCP = 0x06, + /** UDP */ + SD_UDP = 0x11 +}; + +class SomeIpSdLayer; + +/** + * @class SomeIpSdOption + * Base class of the SOME/IP-SD options. Cannot be instantiated. + */ +class SomeIpSdOption +{ +public: + friend class SomeIpSdLayer; + + /** + * Types of options currently available for the SOME/IP-SD protocol + */ + enum class OptionType : uint8_t + { + /** Unknown Option Type */ + Unknown = 0x00, + /** Configuration Option */ + ConfigurationString = 0x01, + /** Load Balancing Option */ + LoadBalancing = 0x02, + /** IPv4 Endpoint Option */ + IPv4Endpoint = 0x04, + /** IPv6 Endpoint Option */ + IPv6Endpoint = 0x06, + /** IPv4 Multicast Option */ + IPv4Multicast = 0x14, + /** IPv6 Multicast Option */ + IPv6Multicast = 0x16, + /** IPv4 SD Endpoint Option */ + IPv4SdEndpoint = 0x24, + /** IPv6 SD Endpoint Option */ + IPv6SdEndpoint = 0x26 + }; + + /** + * @struct someipsdhdroptionsbase + * Represents the common base for SOME/IP-SD header options + */ +#pragma pack(push, 1) + struct someipsdhdroptionsbase + { + /** Length - excluding the 16 bit Length field and the 8 bit type flag */ + uint16_t length; + /** Type */ + uint8_t type; + /** Reserved */ + uint8_t reserved; + }; +#pragma pack(pop) + + /** + * Destroy the SOME/IP-SD Option object and delete allocated data if it has been allocated by a constructor + */ + virtual ~SomeIpSdOption(); + + /** + * Get the Option Type + * @return OptionType + */ + OptionType getType() const; + + /** + * Get the Length of the SOME/IP-SD option + * @return size_t + */ + size_t getLength() const { return m_DataLen; } + + /** + * Get the internal data of the SOME/IP-SD Option + * @return uint8_t* + */ + uint8_t *getDataPtr() const; + + /** + * Get a pointer to the SOME/IP-SD Option base header + * @return someipsdhdroptionsbase* + */ + someipsdhdroptionsbase *getSomeIpSdOptionHeader() const; + +protected: + const IDataContainer *m_DataContainer; + size_t m_Offset; + uint8_t *m_ShadowData; + size_t m_DataLen; + + SomeIpSdOption() : m_DataContainer(nullptr), m_Offset(0), m_ShadowData(nullptr), m_DataLen(0) {} + + SomeIpSdOption(const IDataContainer *dataContainer, size_t offset) + : m_DataContainer(dataContainer), m_Offset(offset), m_ShadowData(nullptr), m_DataLen(0) {} + + void initStdFields(OptionType type); + + SomeIpSdOption(const SomeIpSdOption &) = delete; + SomeIpSdOption &operator=(const SomeIpSdOption &) = delete; +}; + +/** + * @class SomeIpSdIPv4Option + * Implements the following SOME/IP-SD Options: IPv4 Endpoint, IPv4 Multicast, IPv4 SD Endpoint + */ +class SomeIpSdIPv4Option : public SomeIpSdOption +{ +public: + friend class SomeIpSdLayer; + + /** + * Types of options which are implemented with this class + */ + enum IPv4OptionType + { + /** IPv4 Endpoint Option */ + IPv4Endpoint, + /** IPv4 Multicast Option */ + IPv4Multicast, + /** IPv4 SD Endpoint Option */ + IPv4SdEndpoint, + }; + + /** + * Construct a new SomeIpSdIPv4 Option object + * @param[in] type IPv4 Option type + * @param[in] ipAddress Ipv4 address to use + * @param[in] port Port to use + * @param[in] l4Protocol Protocol to use + */ + SomeIpSdIPv4Option(IPv4OptionType type, IPv4Address ipAddress, uint16_t port, SomeIpSdProtocolType l4Protocol); + + /** + * Construct a new SomeIpSdIPv4 Option object from already existing memory + * @param[in] dataContainer Data containing the SomeIpSdIPv4 Option object + * @param[in] offset Offset for dataContainer + */ + SomeIpSdIPv4Option(const IDataContainer *dataContainer, size_t offset); + + /** + * Get the Ip Address + * @return IPv4Address + */ + IPv4Address getIpAddress() const; + + /** + * Get the Port + * @return uint16_t + */ + uint16_t getPort() const; + + /** + * Get the Protocol + * @return SomeIpSdProtocolType + */ + SomeIpSdProtocolType getProtocol() const; + +private: + /** + * @struct someipsdhdroptionsipv4 + * Represents the IPv4 option types for the SOME/IP-SD header + */ +#pragma pack(push, 1) + struct someipsdhdroptionsipv4 : someipsdhdroptionsbase + { + /* IPv4-Address field */ + uint32_t ipv4Address; + /* Reserved */ + // cppcheck-suppress duplInheritedMember + uint8_t reserved; + /* Layer 4 Protocol field (L4-Proto) - Either UDP or TCP */ + SomeIpSdProtocolType l4Protocol; + /* Port number of UDP or TCP */ + uint16_t portNumber; + }; +#pragma pack(pop) +}; + +/** + * @class SomeIpSdIPv6Option + * Implements the following SOME/IP-SD Options: IPv6 Endpoint, IPv6 Multicast, IPv6 SD Endpoint + */ +class SomeIpSdIPv6Option : public SomeIpSdOption +{ +public: + friend class SomeIpSdLayer; + + /** + * Types of options which are implemented with this class + */ + enum IPv6OptionType + { + /** IPv6 Endpoint Option */ + IPv6Endpoint, + /** IPv6 Multicast Option */ + IPv6Multicast, + /** IPv6 SD Endpoint Option */ + IPv6SdEndpoint, + }; + + /** + * Construct a new SomeIpSdIPv6 Option object + * @param[in] type IPv6 Option type + * @param[in] ipAddress Ipv6 address to use + * @param[in] port Port to use + * @param[in] l4Protocol Protocol to use + */ + SomeIpSdIPv6Option(IPv6OptionType type, IPv6Address ipAddress, uint16_t port, SomeIpSdProtocolType l4Protocol); + + /** + * Construct a new SomeIpSdIPv6 Option object from already existing memory + * @param[in] dataContainer Data containing the SomeIpSdIPv6 Option object + * @param[in] offset Offset for dataContainer + */ + SomeIpSdIPv6Option(const IDataContainer *dataContainer, size_t offset); + + /** + * Get the Ip Address + * @return IPv6Address + */ + IPv6Address getIpAddress() const; + + /** + * Get the Port + * @return uint16_t + */ + uint16_t getPort() const; + + /** + * Get the Protocol + * @return SomeIpSdProtocolType + */ + SomeIpSdProtocolType getProtocol() const; + +private: + /** + * @struct someipsdhdroptionsipv6 + * Represents the IPv6 option types for the SOME/IP-SD header + */ +#pragma pack(push, 1) + struct someipsdhdroptionsipv6 : someipsdhdroptionsbase + { + /* IPv6-Address field */ + uint8_t ipv6Address[16]; + /* Reserved */ + // cppcheck-suppress duplInheritedMember + uint8_t reserved; + /* Layer 4 Protocol field (L4-Proto) - Either UDP or TCP */ + SomeIpSdProtocolType l4Protocol; + /* Port number of UDP or TCP */ + uint16_t portNumber; + }; +#pragma pack(pop) +}; + +/** + * @class SomeIpSdConfigurationOption + * Implements the Configuration option of SOME/IP-SD protocol + */ +class SomeIpSdConfigurationOption : public SomeIpSdOption +{ +public: + friend class SomeIpSdLayer; + + /** + * Construct a new Configuration Option object + * @param[in] configurationString the configuration string + */ + explicit SomeIpSdConfigurationOption(const std::string &configurationString); + + /** + * Construct a new Configuration Option object from already existing memory + * @param[in] dataContainer Data containing the Configuration Option object + * @param[in] offset Offset for dataContainer + */ + SomeIpSdConfigurationOption(const IDataContainer *dataContainer, size_t offset); + + /** + * Get the configuration string + * @return std::string + */ + std::string getConfigurationString() const; +}; + +/** + * @class SomeIpSdLoadBalancingOption + * Implements the Load Balancing option of SOME/IP-SD protocol + */ +class SomeIpSdLoadBalancingOption : public SomeIpSdOption +{ +public: + friend class SomeIpSdLayer; + + /** + * Construct a new Load Balancing object + * @param[in] priority Priority of this instance + * @param[in] weight Weight of this instance + */ + SomeIpSdLoadBalancingOption(uint16_t priority, uint16_t weight); + + /** + * Construct a new Option object from already existing memory + * @param[in] dataContainer Data containing the option object + * @param[in] offset Offset for dataContainer + */ + SomeIpSdLoadBalancingOption(const IDataContainer *dataContainer, size_t offset); + + /** + * Get the priority fild + * @return uint16_t + */ + uint16_t getPriority() const; + + /** + * Get the weight field + * @return uint16_t + */ + uint16_t getWeight() const; + +private: + /** + * @struct someipsdhdroptionsload + * Represents the Load Balancing option header for SOME/IP-SD + */ +#pragma pack(push, 1) + struct someipsdhdroptionsload : someipsdhdroptionsbase + { + /* Priority field */ + uint16_t priority; + /* Weight field */ + uint16_t weight; + }; +#pragma pack(pop) +}; + +/** + * @class SomeIpSdEntry + * Implementation of the SOME/IP-SD Service Entry and Eventgroup Entry Type + */ +class SomeIpSdEntry +{ +public: + friend class SomeIpSdLayer; + + /** + * Types of entries that can occur in SOME/IP-SD + */ + enum class EntryType : uint8_t + { + /** Find Service */ + FindService, + /** Offer Service */ + OfferService, + /** Stop Offer Service */ + StopOfferService, + /** Subscribe Eventgroup */ + SubscribeEventgroup, + /** Stop Subscribe Eventgroup */ + StopSubscribeEventgroup, + /** Subscribe Eventgroup Acknowledgment */ + SubscribeEventgroupAck, + /** Subscribe Eventgroup Negative Acknowledgement */ + SubscribeEventgroupNack, + /** Unknown Entry Type */ + UnknownEntryType + }; + + /** + * @struct someipsdhdrentry + * Represents the Service Entry Type and Eventgroup Entry Type + */ +#pragma pack(push, 1) + struct someipsdhdrentry + { + /** Type */ + uint8_t type; + /** Index 1st option */ + uint8_t indexFirstOption; + /** Index 2nd option */ + uint8_t indexSecondOption; +#if (BYTE_ORDER == LITTLE_ENDIAN) + uint8_t + /** Numbers of Option #2 (4bit) */ + nrOpt2 : 4, + /** Numbers of Option #1 (4bit) */ + nrOpt1 : 4; +#else + uint8_t + /** Numbers of Option #1 (4bit) */ + nrOpt1 : 4, + /** Numbers of Option #2 (4bit) */ + nrOpt2 : 4; +#endif + /** Service ID */ + uint16_t serviceID; + /** Instance ID */ + uint16_t instanceID; + /** Major Version (8 bit) + TTL (24 bit) */ + uint32_t majorVersion_ttl; + /** Minor Version (Service Entry Type) or Counter + Eventgroup ID (Eventgroup Entry Type) */ + uint32_t data; + }; +#pragma pack(pop) + + /** + * Construct a new SOME/IP-SD Service Entry Type + * @param[in] type Type to create + * @param[in] serviceID ServiceID to use + * @param[in] instanceID InstanceID to use + * @param[in] majorVersion MajorVersion to use + * @param[in] TTL TTL to use. Has to be 0 for all Stop* entry types + * @param[in] minorVersion MinorVersion to use + */ + SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, uint32_t TTL, + uint32_t minorVersion); + + /** + * Construct a new SOME/IP-SD Eventgroup Entry Type + * @param[in] type Type to create + * @param[in] serviceID ServiceID to use + * @param[in] instanceID InstanceID to use + * @param[in] majorVersion MajorVersion to use + * @param[in] TTL TTL to use. Has to be 0 for all Stop* entry types + * @param[in] counter Counter value to use + * @param[in] eventGroupID EventgroupId to use + */ + SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, uint32_t TTL, + uint8_t counter, uint16_t eventGroupID); + + /** + * Construct a new SomeIpSdEntry object from existing data + * @param[in] pSomeIpSdLayer Layer that this entry is created for + * @param[in] offset Offset for pSomeIpSdLayer + */ + SomeIpSdEntry(const SomeIpSdLayer *pSomeIpSdLayer, size_t offset); + + /** + * Destroy the SomeIpSd Entry object and delete allocated data if it has been allocated by a constructor + */ + ~SomeIpSdEntry(); + + /** + * Get the internal data of the SOME/IP-SD Entry + * @return uint8_t* + */ + uint8_t *getDataPtr() const; + + /** + * Get a pointer to the SOME/IP-SD Entry header + * @return someipsdhdrentry* + */ + someipsdhdrentry *getSomeIpSdEntryHeader() const; + + /** + * Get the Entry Type + * @return EntryType + */ + EntryType getType() const { return m_EntryType; } + + /** + * Get the Length of the SomeIpSd Entry + * @return size_t + */ + size_t getLength() const { return sizeof(someipsdhdrentry); } + + /** + * Get the number of Options of this Entry + * @return uint32_t + */ + uint32_t getNumOptions() const; + + /** + * Get the Service Id in host endianness + * @return uint16_t + */ + uint16_t getServiceId() const; + + /** + * Set the Service Id + * @param[in] serviceId + */ + void setServiceId(uint16_t serviceId); + + /** + * Get the Instance Id in host endianness + * @return uint16_t + */ + uint16_t getInstanceId() const; + + /** + * Set the Instance Id + * @param[in] instanceId + */ + void setInstanceId(uint16_t instanceId); + + /** + * Get the Major version field in host endianness + * @return uint16_t + */ + uint8_t getMajorVersion() const; + + /** + * Set the Major Version + * @param[in] majorVersion + */ + void setMajorVersion(uint8_t majorVersion); + + /** + * Get the Ttl field + * @return uint32_t + */ + uint32_t getTtl() const; + + /** + * Set the Ttl field + * @param[in] ttl + */ + void setTtl(uint32_t ttl); + + /** + * Get the minor version + * @return uint32_t + */ + uint32_t getMinorVersion() const; + + /** + * Set the minor version + * @param[in] minorVersion + */ + void setMinorVersion(uint32_t minorVersion); + + /** + * Get the counter value + * @return uint32_t + */ + uint8_t getCounter() const; + + /** + * Set the counter value + * @param[in] counter + */ + void setCounter(uint8_t counter); + + /** + * Get the eventgroup id + * @return uint32_t + */ + uint16_t getEventgroupId() const; + + /** + * Set the eventgroup id + * @param[in] eventgroupID + */ + void setEventgroupId(uint16_t eventgroupID); + +private: + /** + * These are the entry types used by SOME/IP-SD. They cannot be used for parameter passing since the values + * are not unique. + */ + enum class TypeInternal : uint8_t + { + /** Find Service */ + FindService_Internal = 0x00, + /** Offer Service / Stop Offer Service */ + OfferService_Internal = 0x01, + /** Subscribe Eventgroup & Stop Subscribe Eventgroup */ + SubscribeEventgroup_Internal = 0x06, + /** Subscribe Eventgroup Acknowledgment / Negative Acknowledgement */ + SubscribeEventgroupAck_Internal = 0x07, + }; + + EntryType m_EntryType; + const SomeIpSdLayer *m_Layer; + size_t m_Offset; + uint8_t *m_ShadowData; + + void initStdFields(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, uint32_t TTL); + + SomeIpSdEntry(const SomeIpSdEntry &) = delete; + SomeIpSdEntry &operator=(const SomeIpSdEntry &) = delete; + + static const uint32_t SOMEIPSD_HDR_ENTRY_MASK_TTL = 0x00FFFFFF; +}; + +/** + * @class SomeIpSdLayer + * Implementation of the SOME/IP-SD protocol + */ +class SomeIpSdLayer : public SomeIpLayer +{ +public: + friend class SomeIpSdEntry; + + typedef SomeIpSdEntry* EntryPtr; + typedef std::vector EntriesVec; + typedef SomeIpSdOption* OptionPtr; + typedef std::vector OptionsVec; + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + SomeIpSdLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet); + + /** + * Construct a new SomeIpSdLayer object + * @param[in] serviceID Service ID + * @param[in] methodID Method ID + * @param[in] clientID Client ID + * @param[in] sessionID Session ID + * @param[in] interfaceVersion Interface Version + * @param[in] type Type of the message + * @param[in] returnCode Return Code + * @param[in] flags Flags that shall be used in the header + */ + SomeIpSdLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, + uint8_t interfaceVersion, MsgType type, uint8_t returnCode, uint8_t flags); + + /** + * Destroy the layer object + */ + ~SomeIpSdLayer() {} + + /** + * Checks if given port is a SOME/IP-SD protocol port + * @param[in] port Port to check + * @return true if SOME/IP-SD protocol port, false if not + */ + static bool isSomeIpSdPort(uint16_t port) { return port == 30490; } + + /** + * Get the Flags of the layer + * @return uint8_t Flags + */ + uint8_t getFlags() const; + + /** + * Set the Flags of the layer + * @param[in] flags Flags to set + */ + void setFlags(uint8_t flags); + + /** + * Get the number of entries in this layer + * @return uint32_t + */ + uint32_t getNumEntries() const; + + /** + * Get the number of options in this layer + * @return uint32_t + */ + uint32_t getNumOptions() const; + + /** + * Get the Entries from this layer + * @return EntriesVec Vector holding pointers to the options + */ + const EntriesVec getEntries() const; + + /** + * Get the Options from this layer + * @return OptionsVec Vector holding pointers to the options + */ + const OptionsVec getOptions() const; + + /** + * Get the Options from a specific Entry + * @param[in] index Index of the Entry, starting with 0. + * @return OptionsVec Vector holding pointers to the options + */ + const OptionsVec getOptionsFromEntry(uint32_t index) const; + + /** + * Adds a given entry to the layer and returns the index of the entry + * @param[in] entry Pointer to the entry that shall be added to the layer + * @return uint32_t Returns the index of the entry starting with 0 + */ + uint32_t addEntry(const SomeIpSdEntry &entry); + + /** + * Adds an option to an entry that has already been added to the layer by using addEntry(). The option + * is also added to the layer itself. If the option cannot by assigned to the entry, the option is not + * copied into the layer. + * @param[in] indexEntry Index of the entry where the option shall be added. First Entry has index 0 + * @param[in] option Pointer to the option that shall be added + * @return True if the option could be assigned to the entry and was copied into the layer, false otherwise + */ + bool addOptionTo(uint32_t indexEntry, const SomeIpSdOption &option); + + /** + * Does nothing for this layer + */ + void computeCalculateFields() {}; + + /** + * @return The string representation of the SOME/IP-SD layer + */ + std::string toString() const; + +private: + /** + * @struct someipsdhdr + * Represents an SOME/IP-SD protocol header + */ +#pragma pack(push, 1) + struct someipsdhdr : someiphdr + { + /** Flags (8 bit) */ + uint8_t flags; + /** Reserved1 field (Bits 0-7 of 24-bits reserved field) */ + uint8_t reserved1; + /** Reserved2 field (Bits 8-15 of 24-bits reserved field) */ + uint8_t reserved2; + /** Reserved3 field (Bits 16-23 of 24-bits reserved field) */ + uint8_t reserved3; + }; +#pragma pack(pop) + + uint32_t m_NumOptions; + + uint32_t countOptions(); + uint32_t findOption(const SomeIpSdOption &option); + void addOption(const SomeIpSdOption &option); + bool addOptionIndex(uint32_t indexEntry, uint32_t indexOffset); + OptionPtr parseOption(SomeIpSdOption::OptionType type, size_t offset) const; + + size_t getLenEntries() const; + size_t getLenOptions() const; + void setLenEntries(uint32_t length); + void setLenOptions(uint32_t length); +}; + +} // namespace pcpp +#endif /* PACKETPP_SOMEIPSD_LAYER */ diff --git a/Packet++/header/TcpLayer.h b/Packet++/header/TcpLayer.h index f0de9a9741..eb3d602265 100644 --- a/Packet++/header/TcpLayer.h +++ b/Packet++/header/TcpLayer.h @@ -1,506 +1,506 @@ -#ifndef PACKETPP_TCP_LAYER -#define PACKETPP_TCP_LAYER - -#include "Layer.h" -#include "TLVData.h" -#include - -/// @file - -/** - * \namespace pcpp - * \brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - - /** - * @struct tcphdr - * Represents an TCP protocol header - */ -#pragma pack(push,1) - struct tcphdr - { - /** Source TCP port */ - uint16_t portSrc; - /** Destination TCP port */ - uint16_t portDst; - /** Sequence number */ - uint32_t sequenceNumber; - /** Acknowledgment number */ - uint32_t ackNumber; -#if (BYTE_ORDER == LITTLE_ENDIAN) - uint16_t reserved:4, - /** Specifies the size of the TCP header in 32-bit words */ - dataOffset:4, - /** FIN flag */ - finFlag:1, - /** SYN flag */ - synFlag:1, - /** RST flag */ - rstFlag:1, - /** PSH flag */ - pshFlag:1, - /** ACK flag */ - ackFlag:1, - /** URG flag */ - urgFlag:1, - /** ECE flag */ - eceFlag:1, - /** CWR flag */ - cwrFlag:1; -#elif (BYTE_ORDER == BIG_ENDIAN) - /** Specifies the size of the TCP header in 32-bit words */ - uint16_t dataOffset:4, - reserved:4, - /** CWR flag */ - cwrFlag:1, - /** ECE flag */ - eceFlag:1, - /** URG flag */ - urgFlag:1, - /** ACK flag */ - ackFlag:1, - /** PSH flag */ - pshFlag:1, - /** RST flag */ - rstFlag:1, - /** SYN flag */ - synFlag:1, - /** FIN flag */ - finFlag:1; -#else -#error "Endian is not LE nor BE..." -#endif - /** The size of the receive window, which specifies the number of window size units (by default, bytes) */ - uint16_t windowSize; - /** The 16-bit checksum field is used for error-checking of the header and data */ - uint16_t headerChecksum; - /** If the URG flag (@ref tcphdr#urgFlag) is set, then this 16-bit field is an offset from the sequence number indicating the last urgent data byte */ - uint16_t urgentPointer; - }; -#pragma pack(pop) - - - /** - * TCP options types - */ - enum TcpOptionType - { - /** Padding */ - PCPP_TCPOPT_NOP = 1, - /** End of options */ - PCPP_TCPOPT_EOL = 0, - /** Segment size negotiating */ - TCPOPT_MSS = 2, - /** Window scaling */ - PCPP_TCPOPT_WINDOW = 3, - /** SACK Permitted */ - TCPOPT_SACK_PERM = 4, - /** SACK Block */ - PCPP_TCPOPT_SACK = 5, - /** Echo (obsoleted by option ::PCPP_TCPOPT_TIMESTAMP) */ - TCPOPT_ECHO = 6, - /** Echo Reply (obsoleted by option ::PCPP_TCPOPT_TIMESTAMP) */ - TCPOPT_ECHOREPLY = 7, - /** TCP Timestamps */ - PCPP_TCPOPT_TIMESTAMP = 8, - /** CC (obsolete) */ - TCPOPT_CC = 11, - /** CC.NEW (obsolete) */ - TCPOPT_CCNEW = 12, - /** CC.ECHO(obsolete) */ - TCPOPT_CCECHO = 13, - /** MD5 Signature Option */ - TCPOPT_MD5 = 19, - /** Multipath TCP */ - TCPOPT_MPTCP = 0x1e, - /** SCPS Capabilities */ - TCPOPT_SCPS = 20, - /** SCPS SNACK */ - TCPOPT_SNACK = 21, - /** SCPS Record Boundary */ - TCPOPT_RECBOUND = 22, - /** SCPS Corruption Experienced */ - TCPOPT_CORREXP = 23, - /** Quick-Start Response */ - TCPOPT_QS = 27, - /** User Timeout Option (also, other known unauthorized use) */ - TCPOPT_USER_TO = 28, - /** RFC3692-style Experiment 1 (also improperly used for shipping products) */ - TCPOPT_EXP_FD = 0xfd, - /** RFC3692-style Experiment 2 (also improperly used for shipping products) */ - TCPOPT_EXP_FE = 0xfe, - /** Riverbed probe option, non IANA registered option number */ - TCPOPT_RVBD_PROBE = 76, - /** Riverbed transparency option, non IANA registered option number */ - TCPOPT_RVBD_TRPY = 78, - /** Unknown option */ - TCPOPT_Unknown = 255 - }; - - - // TCP option lengths - - /** pcpp::PCPP_TCPOPT_NOP length */ -#define PCPP_TCPOLEN_NOP 1 - /** pcpp::PCPP_TCPOPT_EOL length */ -#define PCPP_TCPOLEN_EOL 1 - /** pcpp::TCPOPT_MSS length */ -#define PCPP_TCPOLEN_MSS 4 - /** pcpp::PCPP_TCPOPT_WINDOW length */ -#define PCPP_TCPOLEN_WINDOW 3 - /** pcpp::TCPOPT_SACK_PERM length */ -#define PCPP_TCPOLEN_SACK_PERM 2 - /** pcpp::PCPP_TCPOPT_SACK length */ -#define PCPP_TCPOLEN_SACK_MIN 2 - /** pcpp::TCPOPT_ECHO length */ -#define PCPP_TCPOLEN_ECHO 6 - /** pcpp::TCPOPT_ECHOREPLY length */ -#define PCPP_TCPOLEN_ECHOREPLY 6 - /** pcpp::PCPP_TCPOPT_TIMESTAMP length */ -#define PCPP_TCPOLEN_TIMESTAMP 10 - /** pcpp::TCPOPT_CC length */ -#define PCPP_TCPOLEN_CC 6 - /** pcpp::TCPOPT_CCNEW length */ -#define PCPP_TCPOLEN_CCNEW 6 - /** pcpp::TCPOPT_CCECHO length */ -#define PCPP_TCPOLEN_CCECHO 6 - /** pcpp::TCPOPT_MD5 length */ -#define PCPP_TCPOLEN_MD5 18 - /** pcpp::TCPOPT_MPTCP length */ -#define PCPP_TCPOLEN_MPTCP_MIN 8 - /** pcpp::TCPOPT_SCPS length */ -#define PCPP_TCPOLEN_SCPS 4 - /** pcpp::TCPOPT_SNACK length */ -#define PCPP_TCPOLEN_SNACK 6 - /** pcpp::TCPOPT_RECBOUND length */ -#define PCPP_TCPOLEN_RECBOUND 2 - /** pcpp::TCPOPT_CORREXP length */ -#define PCPP_TCPOLEN_CORREXP 2 - /** pcpp::TCPOPT_QS length */ -#define PCPP_TCPOLEN_QS 8 - /** pcpp::TCPOPT_USER_TO length */ -#define PCPP_TCPOLEN_USER_TO 4 - /** pcpp::TCPOPT_RVBD_PROBE length */ -#define PCPP_TCPOLEN_RVBD_PROBE_MIN 3 - /** pcpp::TCPOPT_RVBD_TRPY length */ -#define PCPP_TCPOLEN_RVBD_TRPY_MIN 16 - /** pcpp::TCPOPT_EXP_FD and pcpp::TCPOPT_EXP_FE length */ -#define PCPP_TCPOLEN_EXP_MIN 2 - - - /** - * @class TcpOption - * A wrapper class for TCP options. This class does not create or modify TCP option records, but rather - * serves as a wrapper and provides useful methods for retrieving data from them - */ - class TcpOption : public TLVRecord - { - public: - - /** - * A c'tor for this class that gets a pointer to the option raw data (byte array) - * @param[in] optionRawData A pointer to the TCP option raw data - */ - explicit TcpOption(uint8_t* optionRawData) : TLVRecord(optionRawData) { } - - /** - * A d'tor for this class, currently does nothing - */ - ~TcpOption() { } - - /** - * @return TCP option type casted as pcpp::TcpOptionType enum. If the data is null a value - * of ::TCPOPT_Unknown is returned - */ - TcpOptionType getTcpOptionType() const - { - if (m_Data == NULL) - return TCPOPT_Unknown; - - return (TcpOptionType)m_Data->recordType; - } - - // implement abstract methods - - size_t getTotalSize() const - { - if (m_Data == NULL) - return (size_t)0; - - if (m_Data->recordType == (uint8_t)PCPP_TCPOPT_NOP || m_Data->recordType == (uint8_t)PCPP_TCPOPT_EOL) - return sizeof(uint8_t); - - return (size_t)m_Data->recordLen; - } - - size_t getDataSize() const - { - if (m_Data == NULL) - return 0; - - if (m_Data->recordType == (uint8_t)PCPP_TCPOPT_NOP || m_Data->recordType == (uint8_t)PCPP_TCPOPT_EOL) - return (size_t)0; - - return (size_t)m_Data->recordLen - (2*sizeof(uint8_t)); - } - }; - - - /** - * @class TcpOptionBuilder - * A class for building TCP option records. This builder receives the TCP option parameters in its c'tor, - * builds the TCP option raw buffer and provides a build() method to get a TcpOption object out of it - */ - class TcpOptionBuilder : public TLVRecordBuilder - { - - public: - - /** - * An enum to describe NOP and EOL TCP options. Used in one of this class's c'tors - */ - enum NopEolOptionTypes - { - /** NOP TCP option */ - NOP, - /** EOL TCP option */ - EOL - }; - - /** - * A c'tor for building TCP options which their value is a byte array. The TcpOption object can be later - * retrieved by calling build() - * @param[in] optionType TCP option type - * @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in any way. - * @param[in] optionValueLen Option value length in bytes - */ - TcpOptionBuilder(TcpOptionType optionType, const uint8_t* optionValue, uint8_t optionValueLen) : - TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) {} - - /** - * A c'tor for building TCP options which have a 1-byte value. The TcpOption object can be later retrieved - * by calling build() - * @param[in] optionType TCP option type - * @param[in] optionValue A 1-byte option value - */ - TcpOptionBuilder(TcpOptionType optionType, uint8_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) {} - - /** - * A c'tor for building TCP options which have a 2-byte value. The TcpOption object can be later retrieved - * by calling build() - * @param[in] optionType TCP option type - * @param[in] optionValue A 2-byte option value - */ - TcpOptionBuilder(TcpOptionType optionType, uint16_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) {} - - /** - * A c'tor for building TCP options which have a 4-byte value. The TcpOption object can be later retrieved - * by calling build() - * @param[in] optionType TCP option type - * @param[in] optionValue A 4-byte option value - */ - TcpOptionBuilder(TcpOptionType optionType, uint32_t optionValue) : - TLVRecordBuilder((uint8_t)optionType, optionValue) {} - - /** - * A c'tor for building TCP NOP and EOL options. These option types are special in that they contain only 1 byte - * which is the TCP option type (NOP or EOL). The TcpOption object can be later retrieved - * by calling build() - * @param[in] optionType An enum value indicating which option type to build (NOP or EOL) - */ - explicit TcpOptionBuilder(NopEolOptionTypes optionType); - - /** - * Build the TcpOption object out of the parameters defined in the c'tor - * @return The TcpOption object - */ - TcpOption build() const; - }; - - - /** - * @class TcpLayer - * Represents a TCP (Transmission Control Protocol) protocol layer - */ - class TcpLayer : public Layer - { - public: - /** - * A constructor that creates the layer from an existing packet raw data - * @param[in] data A pointer to the raw data (will be casted to @ref tcphdr) - * @param[in] dataLen Size of the data in bytes - * @param[in] prevLayer A pointer to the previous layer - * @param[in] packet A pointer to the Packet instance where layer will be stored in - */ - TcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); - - /** - * A constructor that allocates a new TCP header with zero TCP options - */ - TcpLayer(); - - /** - * A constructor that allocates a new TCP header with source port and destination port and zero TCP options - * @param[in] portSrc Source port - * @param[in] portDst Destination port - */ - TcpLayer(uint16_t portSrc, uint16_t portDst); - - ~TcpLayer() {} - - /** - * A copy constructor that copy the entire header from the other TcpLayer (including TCP options) - */ - TcpLayer(const TcpLayer& other); - - /** - * An assignment operator that first delete all data from current layer and then copy the entire header from the other TcpLayer (including TCP options) - */ - TcpLayer& operator=(const TcpLayer& other); - - /** - * Get a pointer to the TCP header. Notice this points directly to the data, so every change will change the actual packet data - * @return A pointer to the @ref tcphdr - */ - tcphdr* getTcpHeader() const { return (tcphdr*)m_Data; } - - /** - * @return TCP source port - */ - uint16_t getSrcPort() const; - - /** - * @return TCP destination port - */ - uint16_t getDstPort() const; - - /** - * Get a TCP option by type - * @param[in] option TCP option type to retrieve - * @return An TcpOption object that contains the first option that matches this type, or logical NULL - * (TcpOption#isNull() == true) if no such option found - */ - TcpOption getTcpOption(TcpOptionType option) const; - - /** - * @return The first TCP option in the packet. If the current layer contains no options the returned value will contain - * a logical NULL (TcpOption#isNull() == true) - */ - TcpOption getFirstTcpOption() const; - - /** - * Get the TCP option that comes after a given option. If the given option was the last one, the - * returned value will contain a logical NULL (TcpOption#isNull() == true) - * @param[in] tcpOption A TCP option object that exists in the current layer - * @return A TcpOption object that contains the TCP option data that comes next, or logical NULL if the given - * TCP option: (1) was the last one; or (2) contains a logical NULL; or (3) doesn't belong to this packet - */ - TcpOption getNextTcpOption(TcpOption& tcpOption) const; - - /** - * @return The number of TCP options in this layer - */ - size_t getTcpOptionCount() const; - - /** - * Add a new TCP option at the end of the layer (after the last TCP option) - * @param[in] optionBuilder A TcpOptionBuilder object that contains the TCP option data to be added - * @return A TcpOption object that contains the newly added TCP option data or logical NULL - * (TcpOption#isNull() == true) if addition failed. In case of a failure a corresponding error message will be - * printed to log - */ - TcpOption addTcpOption(const TcpOptionBuilder& optionBuilder); - - /** - * Add a new TCP option after an existing one - * @param[in] optionBuilder A TcpOptionBuilder object that contains the requested TCP option data to be added - * @param[in] prevOptionType The TCP option which the newly added option should come after. This is an optional parameter which - * gets a default value of ::TCPOPT_Unknown if omitted, which means the new option will be added as the first option in the layer - * @return A TcpOption object containing the newly added TCP option data or logical NULL - * (TcpOption#isNull() == true) if addition failed. In case of a failure a corresponding error message will be - * printed to log - */ - TcpOption addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionType prevOptionType = TCPOPT_Unknown); - - /** - * Remove an existing TCP option from the layer. TCP option is found by type - * @param[in] optionType The TCP option type to remove - * @return True if TCP option was removed or false if type wasn't found or if removal failed (in each case a proper error - * will be written to log) - */ - bool removeTcpOption(TcpOptionType optionType); - - /** - * Remove all TCP options in this layer - * @return True if all TCP options were successfully removed or false if removal failed for some reason - * (a proper error will be written to log) - */ - bool removeAllTcpOptions(); - - - /** - * Calculate the checksum from header and data and possibly write the result to @ref tcphdr#headerChecksum - * @param[in] writeResultToPacket If set to true then checksum result will be written to @ref tcphdr#headerChecksum - * @return The checksum result - */ - uint16_t calculateChecksum(bool writeResultToPacket); - - /** - * The static method makes validation of input data - * @param[in] data The pointer to the beginning of byte stream of TCP packet - * @param[in] dataLen The length of byte stream - * @return True if the data is valid and can represent a TCP packet - */ - static inline bool isDataValid(const uint8_t* data, size_t dataLen); - - // implement abstract methods - - /** - * Currently identifies the following next layers: HttpRequestLayer, HttpResponseLayer. Otherwise sets PayloadLayer - */ - void parseNextLayer(); - - /** - * @return Size of @ref tcphdr + all TCP options - */ - size_t getHeaderLen() const { return getTcpHeader()->dataOffset*4 ;} - - /** - * Calculate @ref tcphdr#headerChecksum field - */ - void computeCalculateFields(); - - std::string toString() const; - - OsiModelLayer getOsiModelLayer() const { return OsiModelTransportLayer; } - - private: - - TLVRecordReader m_OptionReader; - int m_NumOfTrailingBytes; - - void initLayer(); - uint8_t* getOptionsBasePtr() const { return m_Data + sizeof(tcphdr); } - TcpOption addTcpOptionAt(const TcpOptionBuilder& optionBuilder, int offset); - void adjustTcpOptionTrailer(size_t totalOptSize); - void copyLayerData(const TcpLayer& other); - }; - - - // implementation of inline methods - - bool TcpLayer::isDataValid(const uint8_t* data, size_t dataLen) - { - const tcphdr* hdr = reinterpret_cast(data); - return dataLen >= sizeof(tcphdr) - && hdr->dataOffset >= 5 /* the minimum TCP header size */ - && dataLen >= hdr->dataOffset * sizeof(uint32_t); - } - -} // namespace pcpp - -#endif /* PACKETPP_TCP_LAYER */ +#ifndef PACKETPP_TCP_LAYER +#define PACKETPP_TCP_LAYER + +#include "Layer.h" +#include "TLVData.h" +#include + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + + /** + * @struct tcphdr + * Represents an TCP protocol header + */ +#pragma pack(push,1) + struct tcphdr + { + /** Source TCP port */ + uint16_t portSrc; + /** Destination TCP port */ + uint16_t portDst; + /** Sequence number */ + uint32_t sequenceNumber; + /** Acknowledgment number */ + uint32_t ackNumber; +#if (BYTE_ORDER == LITTLE_ENDIAN) + uint16_t reserved:4, + /** Specifies the size of the TCP header in 32-bit words */ + dataOffset:4, + /** FIN flag */ + finFlag:1, + /** SYN flag */ + synFlag:1, + /** RST flag */ + rstFlag:1, + /** PSH flag */ + pshFlag:1, + /** ACK flag */ + ackFlag:1, + /** URG flag */ + urgFlag:1, + /** ECE flag */ + eceFlag:1, + /** CWR flag */ + cwrFlag:1; +#elif (BYTE_ORDER == BIG_ENDIAN) + /** Specifies the size of the TCP header in 32-bit words */ + uint16_t dataOffset:4, + reserved:4, + /** CWR flag */ + cwrFlag:1, + /** ECE flag */ + eceFlag:1, + /** URG flag */ + urgFlag:1, + /** ACK flag */ + ackFlag:1, + /** PSH flag */ + pshFlag:1, + /** RST flag */ + rstFlag:1, + /** SYN flag */ + synFlag:1, + /** FIN flag */ + finFlag:1; +#else +#error "Endian is not LE nor BE..." +#endif + /** The size of the receive window, which specifies the number of window size units (by default, bytes) */ + uint16_t windowSize; + /** The 16-bit checksum field is used for error-checking of the header and data */ + uint16_t headerChecksum; + /** If the URG flag (@ref tcphdr#urgFlag) is set, then this 16-bit field is an offset from the sequence number indicating the last urgent data byte */ + uint16_t urgentPointer; + }; +#pragma pack(pop) + + + /** + * TCP options types + */ + enum TcpOptionType + { + /** Padding */ + PCPP_TCPOPT_NOP = 1, + /** End of options */ + PCPP_TCPOPT_EOL = 0, + /** Segment size negotiating */ + TCPOPT_MSS = 2, + /** Window scaling */ + PCPP_TCPOPT_WINDOW = 3, + /** SACK Permitted */ + TCPOPT_SACK_PERM = 4, + /** SACK Block */ + PCPP_TCPOPT_SACK = 5, + /** Echo (obsoleted by option ::PCPP_TCPOPT_TIMESTAMP) */ + TCPOPT_ECHO = 6, + /** Echo Reply (obsoleted by option ::PCPP_TCPOPT_TIMESTAMP) */ + TCPOPT_ECHOREPLY = 7, + /** TCP Timestamps */ + PCPP_TCPOPT_TIMESTAMP = 8, + /** CC (obsolete) */ + TCPOPT_CC = 11, + /** CC.NEW (obsolete) */ + TCPOPT_CCNEW = 12, + /** CC.ECHO(obsolete) */ + TCPOPT_CCECHO = 13, + /** MD5 Signature Option */ + TCPOPT_MD5 = 19, + /** Multipath TCP */ + TCPOPT_MPTCP = 0x1e, + /** SCPS Capabilities */ + TCPOPT_SCPS = 20, + /** SCPS SNACK */ + TCPOPT_SNACK = 21, + /** SCPS Record Boundary */ + TCPOPT_RECBOUND = 22, + /** SCPS Corruption Experienced */ + TCPOPT_CORREXP = 23, + /** Quick-Start Response */ + TCPOPT_QS = 27, + /** User Timeout Option (also, other known unauthorized use) */ + TCPOPT_USER_TO = 28, + /** RFC3692-style Experiment 1 (also improperly used for shipping products) */ + TCPOPT_EXP_FD = 0xfd, + /** RFC3692-style Experiment 2 (also improperly used for shipping products) */ + TCPOPT_EXP_FE = 0xfe, + /** Riverbed probe option, non IANA registered option number */ + TCPOPT_RVBD_PROBE = 76, + /** Riverbed transparency option, non IANA registered option number */ + TCPOPT_RVBD_TRPY = 78, + /** Unknown option */ + TCPOPT_Unknown = 255 + }; + + + // TCP option lengths + + /** pcpp::PCPP_TCPOPT_NOP length */ +#define PCPP_TCPOLEN_NOP 1 + /** pcpp::PCPP_TCPOPT_EOL length */ +#define PCPP_TCPOLEN_EOL 1 + /** pcpp::TCPOPT_MSS length */ +#define PCPP_TCPOLEN_MSS 4 + /** pcpp::PCPP_TCPOPT_WINDOW length */ +#define PCPP_TCPOLEN_WINDOW 3 + /** pcpp::TCPOPT_SACK_PERM length */ +#define PCPP_TCPOLEN_SACK_PERM 2 + /** pcpp::PCPP_TCPOPT_SACK length */ +#define PCPP_TCPOLEN_SACK_MIN 2 + /** pcpp::TCPOPT_ECHO length */ +#define PCPP_TCPOLEN_ECHO 6 + /** pcpp::TCPOPT_ECHOREPLY length */ +#define PCPP_TCPOLEN_ECHOREPLY 6 + /** pcpp::PCPP_TCPOPT_TIMESTAMP length */ +#define PCPP_TCPOLEN_TIMESTAMP 10 + /** pcpp::TCPOPT_CC length */ +#define PCPP_TCPOLEN_CC 6 + /** pcpp::TCPOPT_CCNEW length */ +#define PCPP_TCPOLEN_CCNEW 6 + /** pcpp::TCPOPT_CCECHO length */ +#define PCPP_TCPOLEN_CCECHO 6 + /** pcpp::TCPOPT_MD5 length */ +#define PCPP_TCPOLEN_MD5 18 + /** pcpp::TCPOPT_MPTCP length */ +#define PCPP_TCPOLEN_MPTCP_MIN 8 + /** pcpp::TCPOPT_SCPS length */ +#define PCPP_TCPOLEN_SCPS 4 + /** pcpp::TCPOPT_SNACK length */ +#define PCPP_TCPOLEN_SNACK 6 + /** pcpp::TCPOPT_RECBOUND length */ +#define PCPP_TCPOLEN_RECBOUND 2 + /** pcpp::TCPOPT_CORREXP length */ +#define PCPP_TCPOLEN_CORREXP 2 + /** pcpp::TCPOPT_QS length */ +#define PCPP_TCPOLEN_QS 8 + /** pcpp::TCPOPT_USER_TO length */ +#define PCPP_TCPOLEN_USER_TO 4 + /** pcpp::TCPOPT_RVBD_PROBE length */ +#define PCPP_TCPOLEN_RVBD_PROBE_MIN 3 + /** pcpp::TCPOPT_RVBD_TRPY length */ +#define PCPP_TCPOLEN_RVBD_TRPY_MIN 16 + /** pcpp::TCPOPT_EXP_FD and pcpp::TCPOPT_EXP_FE length */ +#define PCPP_TCPOLEN_EXP_MIN 2 + + + /** + * @class TcpOption + * A wrapper class for TCP options. This class does not create or modify TCP option records, but rather + * serves as a wrapper and provides useful methods for retrieving data from them + */ + class TcpOption : public TLVRecord + { + public: + + /** + * A c'tor for this class that gets a pointer to the option raw data (byte array) + * @param[in] optionRawData A pointer to the TCP option raw data + */ + explicit TcpOption(uint8_t* optionRawData) : TLVRecord(optionRawData) { } + + /** + * A d'tor for this class, currently does nothing + */ + ~TcpOption() { } + + /** + * @return TCP option type casted as pcpp::TcpOptionType enum. If the data is null a value + * of ::TCPOPT_Unknown is returned + */ + TcpOptionType getTcpOptionType() const + { + if (m_Data == NULL) + return TCPOPT_Unknown; + + return (TcpOptionType)m_Data->recordType; + } + + // implement abstract methods + + size_t getTotalSize() const + { + if (m_Data == NULL) + return (size_t)0; + + if (m_Data->recordType == (uint8_t)PCPP_TCPOPT_NOP || m_Data->recordType == (uint8_t)PCPP_TCPOPT_EOL) + return sizeof(uint8_t); + + return (size_t)m_Data->recordLen; + } + + size_t getDataSize() const + { + if (m_Data == NULL) + return 0; + + if (m_Data->recordType == (uint8_t)PCPP_TCPOPT_NOP || m_Data->recordType == (uint8_t)PCPP_TCPOPT_EOL) + return (size_t)0; + + return (size_t)m_Data->recordLen - (2*sizeof(uint8_t)); + } + }; + + + /** + * @class TcpOptionBuilder + * A class for building TCP option records. This builder receives the TCP option parameters in its c'tor, + * builds the TCP option raw buffer and provides a build() method to get a TcpOption object out of it + */ + class TcpOptionBuilder : public TLVRecordBuilder + { + + public: + + /** + * An enum to describe NOP and EOL TCP options. Used in one of this class's c'tors + */ + enum NopEolOptionTypes + { + /** NOP TCP option */ + NOP, + /** EOL TCP option */ + EOL + }; + + /** + * A c'tor for building TCP options which their value is a byte array. The TcpOption object can be later + * retrieved by calling build() + * @param[in] optionType TCP option type + * @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in any way. + * @param[in] optionValueLen Option value length in bytes + */ + TcpOptionBuilder(TcpOptionType optionType, const uint8_t* optionValue, uint8_t optionValueLen) : + TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) {} + + /** + * A c'tor for building TCP options which have a 1-byte value. The TcpOption object can be later retrieved + * by calling build() + * @param[in] optionType TCP option type + * @param[in] optionValue A 1-byte option value + */ + TcpOptionBuilder(TcpOptionType optionType, uint8_t optionValue) : + TLVRecordBuilder((uint8_t)optionType, optionValue) {} + + /** + * A c'tor for building TCP options which have a 2-byte value. The TcpOption object can be later retrieved + * by calling build() + * @param[in] optionType TCP option type + * @param[in] optionValue A 2-byte option value + */ + TcpOptionBuilder(TcpOptionType optionType, uint16_t optionValue) : + TLVRecordBuilder((uint8_t)optionType, optionValue) {} + + /** + * A c'tor for building TCP options which have a 4-byte value. The TcpOption object can be later retrieved + * by calling build() + * @param[in] optionType TCP option type + * @param[in] optionValue A 4-byte option value + */ + TcpOptionBuilder(TcpOptionType optionType, uint32_t optionValue) : + TLVRecordBuilder((uint8_t)optionType, optionValue) {} + + /** + * A c'tor for building TCP NOP and EOL options. These option types are special in that they contain only 1 byte + * which is the TCP option type (NOP or EOL). The TcpOption object can be later retrieved + * by calling build() + * @param[in] optionType An enum value indicating which option type to build (NOP or EOL) + */ + explicit TcpOptionBuilder(NopEolOptionTypes optionType); + + /** + * Build the TcpOption object out of the parameters defined in the c'tor + * @return The TcpOption object + */ + TcpOption build() const; + }; + + + /** + * @class TcpLayer + * Represents a TCP (Transmission Control Protocol) protocol layer + */ + class TcpLayer : public Layer + { + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref tcphdr) + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + TcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * A constructor that allocates a new TCP header with zero TCP options + */ + TcpLayer(); + + /** + * A constructor that allocates a new TCP header with source port and destination port and zero TCP options + * @param[in] portSrc Source port + * @param[in] portDst Destination port + */ + TcpLayer(uint16_t portSrc, uint16_t portDst); + + ~TcpLayer() {} + + /** + * A copy constructor that copy the entire header from the other TcpLayer (including TCP options) + */ + TcpLayer(const TcpLayer& other); + + /** + * An assignment operator that first delete all data from current layer and then copy the entire header from the other TcpLayer (including TCP options) + */ + TcpLayer& operator=(const TcpLayer& other); + + /** + * Get a pointer to the TCP header. Notice this points directly to the data, so every change will change the actual packet data + * @return A pointer to the @ref tcphdr + */ + tcphdr* getTcpHeader() const { return (tcphdr*)m_Data; } + + /** + * @return TCP source port + */ + uint16_t getSrcPort() const; + + /** + * @return TCP destination port + */ + uint16_t getDstPort() const; + + /** + * Get a TCP option by type + * @param[in] option TCP option type to retrieve + * @return An TcpOption object that contains the first option that matches this type, or logical NULL + * (TcpOption#isNull() == true) if no such option found + */ + TcpOption getTcpOption(TcpOptionType option) const; + + /** + * @return The first TCP option in the packet. If the current layer contains no options the returned value will contain + * a logical NULL (TcpOption#isNull() == true) + */ + TcpOption getFirstTcpOption() const; + + /** + * Get the TCP option that comes after a given option. If the given option was the last one, the + * returned value will contain a logical NULL (TcpOption#isNull() == true) + * @param[in] tcpOption A TCP option object that exists in the current layer + * @return A TcpOption object that contains the TCP option data that comes next, or logical NULL if the given + * TCP option: (1) was the last one; or (2) contains a logical NULL; or (3) doesn't belong to this packet + */ + TcpOption getNextTcpOption(TcpOption& tcpOption) const; + + /** + * @return The number of TCP options in this layer + */ + size_t getTcpOptionCount() const; + + /** + * Add a new TCP option at the end of the layer (after the last TCP option) + * @param[in] optionBuilder A TcpOptionBuilder object that contains the TCP option data to be added + * @return A TcpOption object that contains the newly added TCP option data or logical NULL + * (TcpOption#isNull() == true) if addition failed. In case of a failure a corresponding error message will be + * printed to log + */ + TcpOption addTcpOption(const TcpOptionBuilder& optionBuilder); + + /** + * Add a new TCP option after an existing one + * @param[in] optionBuilder A TcpOptionBuilder object that contains the requested TCP option data to be added + * @param[in] prevOptionType The TCP option which the newly added option should come after. This is an optional parameter which + * gets a default value of ::TCPOPT_Unknown if omitted, which means the new option will be added as the first option in the layer + * @return A TcpOption object containing the newly added TCP option data or logical NULL + * (TcpOption#isNull() == true) if addition failed. In case of a failure a corresponding error message will be + * printed to log + */ + TcpOption addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionType prevOptionType = TCPOPT_Unknown); + + /** + * Remove an existing TCP option from the layer. TCP option is found by type + * @param[in] optionType The TCP option type to remove + * @return True if TCP option was removed or false if type wasn't found or if removal failed (in each case a proper error + * will be written to log) + */ + bool removeTcpOption(TcpOptionType optionType); + + /** + * Remove all TCP options in this layer + * @return True if all TCP options were successfully removed or false if removal failed for some reason + * (a proper error will be written to log) + */ + bool removeAllTcpOptions(); + + + /** + * Calculate the checksum from header and data and possibly write the result to @ref tcphdr#headerChecksum + * @param[in] writeResultToPacket If set to true then checksum result will be written to @ref tcphdr#headerChecksum + * @return The checksum result + */ + uint16_t calculateChecksum(bool writeResultToPacket); + + /** + * The static method makes validation of input data + * @param[in] data The pointer to the beginning of byte stream of TCP packet + * @param[in] dataLen The length of byte stream + * @return True if the data is valid and can represent a TCP packet + */ + static inline bool isDataValid(const uint8_t* data, size_t dataLen); + + // implement abstract methods + + /** + * Currently identifies the following next layers: HttpRequestLayer, HttpResponseLayer. Otherwise sets PayloadLayer + */ + void parseNextLayer(); + + /** + * @return Size of @ref tcphdr + all TCP options + */ + size_t getHeaderLen() const { return getTcpHeader()->dataOffset*4 ;} + + /** + * Calculate @ref tcphdr#headerChecksum field + */ + void computeCalculateFields(); + + std::string toString() const; + + OsiModelLayer getOsiModelLayer() const { return OsiModelTransportLayer; } + + private: + + TLVRecordReader m_OptionReader; + int m_NumOfTrailingBytes; + + void initLayer(); + uint8_t* getOptionsBasePtr() const { return m_Data + sizeof(tcphdr); } + TcpOption addTcpOptionAt(const TcpOptionBuilder& optionBuilder, int offset); + void adjustTcpOptionTrailer(size_t totalOptSize); + void copyLayerData(const TcpLayer& other); + }; + + + // implementation of inline methods + + bool TcpLayer::isDataValid(const uint8_t* data, size_t dataLen) + { + const tcphdr* hdr = reinterpret_cast(data); + return dataLen >= sizeof(tcphdr) + && hdr->dataOffset >= 5 /* the minimum TCP header size */ + && dataLen >= hdr->dataOffset * sizeof(uint32_t); + } + +} // namespace pcpp + +#endif /* PACKETPP_TCP_LAYER */ diff --git a/Packet++/header/TcpReassembly.h b/Packet++/header/TcpReassembly.h index 8a1ac1c018..746ab4ac42 100644 --- a/Packet++/header/TcpReassembly.h +++ b/Packet++/header/TcpReassembly.h @@ -1,460 +1,460 @@ -#ifndef PACKETPP_TCP_REASSEMBLY -#define PACKETPP_TCP_REASSEMBLY - -#include "Packet.h" -#include "IpAddress.h" -#include "PointerVector.h" -#include -#include -#include - - -/** - * @file - * This is an implementation of TCP reassembly logic, which means reassembly of TCP messages spanning multiple TCP segments (or packets).
- * This logic can be useful in analyzing messages for a large number of protocols implemented on top of TCP including HTTP, SSL/TLS, FTP and many many more. - * - * __General Features:__ - * - Manage multiple TCP connections under one pcpp#TcpReassembly instance - * - Support TCP retransmission - * - Support out-of-order packets - * - Support missing TCP data - * - TCP connections can end "naturally" (by FIN/RST packets) or manually by the user - * - Support callbacks for new TCP data, connection start and connection end - * - * __Logic Description:__ - * - The user creates an instance of the pcpp#TcpReassembly class - * - Then the user starts feeding it with TCP packets - * - The pcpp#TcpReassembly instance manages all TCP connections from the packets it's being fed. For each connection it manages its 2 sides (A->B and B->A) - * - When a packet arrives, it is first classified to a certain TCP connection - * - Then it is classified to a certain side of the TCP connection - * - Then the pcpp#TcpReassembly logic tries to understand if the data in this packet is the expected data (sequence-wise) and if it's new (e.g isn't a retransmission) - * - If the packet data matches these criteria a callback is being invoked. This callback is supplied by the user in the creation of the pcpp#TcpReassembly instance. This callback contains - * the new data (of course), but also information about the connection (5-tuple, 4-byte hash key describing the connection, etc.) and also a pointer to a "user cookie", meaning a pointer to - * a structure provided by the user during the creation of the pcpp#TcpReassembly instance - * - If the data in this packet isn't new, it's being ignored - * - If the data in this packet isn't expected (meaning this packet came out-of-order), then the data is being queued internally and will be sent to the user when its turn arrives - * (meaning, after the data before arrives) - * - If the missing data doesn't arrive until a new message from the other side of the connection arrives or until the connection ends - this will be considered as missing data and the - * queued data will be sent to the user, but the string "[X bytes missing]" will be added to the message sent in the callback - * - pcpp#TcpReassembly supports 2 more callbacks - one is invoked when a new TCP connection is first seen and the other when it's ended (either by a FIN/RST packet or manually by the user). - * Both of these callbacks contain data about the connection (5-tuple, 4-byte hash key describing the connection, etc.) and also a pointer to a "user cookie", meaning a pointer to a - * structure provided by the user during the creation of the pcpp#TcpReassembly instance. The end connection callback also provides the reason for closing it ("naturally" or manually) - * - * __Basic Usage and APIs:__ - * - pcpp#TcpReassembly c'tor - Create an instance, provide the callbacks and the user cookie to the instance - * - pcpp#TcpReassembly#reassemblePacket() - Feed pcpp#TcpReassembly instance with packets - * - pcpp#TcpReassembly#closeConnection() - Manually close a connection by a flow key - * - pcpp#TcpReassembly#closeAllConnections() - Manually close all currently opened connections - * - pcpp#TcpReassembly#OnTcpMessageReady callback - Invoked when new data arrives on a certain connection. Contains the new data as well as connection data (5-tuple, flow key) - * - pcpp#TcpReassembly#OnTcpConnectionStart callback - Invoked when a new connection is identified - * - pcpp#TcpReassembly#OnTcpConnectionEnd callback - Invoked when a connection ends (either by FIN/RST or manually by the user) - * - * __Additional information:__ - * When the connection is closed the information is not being deleted from memory immediately. There is a delay between these moments. Existence of this delay is caused by two reasons: - * - pcpp#TcpReassembly#reassemblePacket() should detect the packets that arrive after the FIN packet has been received - * - the user can use the information about connections managed by pcpp#TcpReassembly instance. Following methods are used for this purpose: pcpp#TcpReassembly#getConnectionInformation and pcpp#TcpReassembly#isConnectionOpen. - * Cleaning of memory can be performed automatically (the default behavior) by pcpp#TcpReassembly#reassemblePacket() or manually by calling pcpp#TcpReassembly#purgeClosedConnections in the user code. - * Automatic cleaning is performed once per second. - * - * The struct pcpp#TcpReassemblyConfiguration allows to setup the parameters of cleanup. Following parameters are supported: - * - pcpp#TcpReassemblyConfiguration#doNotRemoveConnInfo - if this member is set to false the automatic cleanup mode is applied - * - pcpp#TcpReassemblyConfiguration#closedConnectionDelay - the value of delay expressed in seconds. The minimum value is 1 - * - pcpp#TcpReassemblyConfiguration#maxNumToClean - to avoid performance overhead when the cleanup is being performed, this parameter is used. It defines the maximum number of items to be removed per one call of pcpp#TcpReassembly#purgeClosedConnections - * - pcpp#TcpReassemblyConfiguration#maxOutOfOrderFragments - the maximum number of unmatched fragments to keep per flow before missed fragments are considered lost. A value of 0 means unlimited - * - */ - -/** - * @namespace pcpp - * @brief The main namespace for the PcapPlusPlus lib - */ -namespace pcpp -{ - -/** - * @struct ConnectionData - * Represents basic TCP/UDP + IP connection data - */ -struct ConnectionData -{ - /** Source IP address */ - IPAddress srcIP; - /** Destination IP address */ - IPAddress dstIP; - /** Source TCP/UDP port */ - uint16_t srcPort; - /** Destination TCP/UDP port */ - uint16_t dstPort; - /** A 4-byte hash key representing the connection */ - uint32_t flowKey; - /** Start TimeStamp of the connection */ - timeval startTime; - /** End TimeStamp of the connection */ - timeval endTime; - - /** - * A c'tor for this struct that basically zeros all members - */ - ConnectionData() : srcPort(0), dstPort(0), flowKey(0), startTime(), endTime() {} - - /** - * Set startTime of Connection - * @param[in] startTime integer value - */ - void setStartTime(const timeval &startTime) { this->startTime = startTime; } - - /** - * Set endTime of Connection - * @param[in] endTime integer value - */ - void setEndTime(const timeval &endTime) { this->endTime = endTime; } -}; - - -class TcpReassembly; - - -/** - * @class TcpStreamData - * When following a TCP connection each packet may contain a piece of the data transferred between the client and the server. This class represents these pieces: each instance of it - * contains a piece of data, usually extracted from a single packet, as well as information about the connection - */ -class TcpStreamData -{ -public: - /** - * A c'tor for this class that get data from outside and set the internal members - * @param[in] tcpData A pointer to buffer containing the TCP data piece - * @param[in] tcpDataLength The length of the buffer - * @param[in] missingBytes The number of missing bytes due to packet loss. - * @param[in] connData TCP connection information for this TCP data - * @param[in] timestamp when this packet was received - */ - TcpStreamData(const uint8_t* tcpData, size_t tcpDataLength, size_t missingBytes, const ConnectionData& connData, timeval timestamp) - : m_Data(tcpData), m_DataLen(tcpDataLength), m_MissingBytes(missingBytes), m_Connection(connData), m_Timestamp(timestamp) - { - } - - /** - * A getter for the data buffer - * @return A pointer to the buffer - */ - const uint8_t* getData() const { return m_Data; } - - /** - * A getter for buffer length - * @return Buffer length - */ - size_t getDataLength() const { return m_DataLen; } - - /** - * A getter for missing byte count due to packet loss. - * @return Missing byte count - */ - size_t getMissingByteCount() const { return m_MissingBytes; } - - /** - * Determine if bytes are missing. getMissingByteCount can be called to determine the number of missing bytes. - * @return true if bytes are missing. - */ - bool isBytesMissing() const { return getMissingByteCount() > 0; } - - /** - * A getter for the connection data - * @return The const reference to connection data - */ - const ConnectionData& getConnectionData() const { return m_Connection; } - - /** - * A getter for the timestamp of this packet - * @return The const timeval object with timestamp of this packet - */ - timeval getTimeStamp() const { return m_Timestamp; } - -private: - const uint8_t* m_Data; - size_t m_DataLen; - size_t m_MissingBytes; - const ConnectionData& m_Connection; - timeval m_Timestamp; -}; - - -/** - * @struct TcpReassemblyConfiguration - * A structure for configuring the TcpReassembly class - */ -struct TcpReassemblyConfiguration -{ - /** The flag indicating whether to remove the connection data after a connection is closed */ - bool removeConnInfo; - - /** How long the closed connections will not be cleaned up. The value is expressed in seconds. If the value is set to 0 then TcpReassembly should use the default value. - * This parameter is only relevant if removeConnInfo is equal to true. - */ - uint32_t closedConnectionDelay; - - /** The maximum number of items to be cleaned up per one call of purgeClosedConnections. If the value is set to 0 then TcpReassembly should use the default value. - * This parameter is only relevant if removeConnInfo is equal to true. - */ - uint32_t maxNumToClean; - - /** The maximum number of fragments with a non-matching sequence-number to store per connection flow before packets are assumed permanently missed. - If the value is 0, TcpReassembly should keep out of order fragments indefinitely, or until a message from the paired side is seen. - */ - uint32_t maxOutOfOrderFragments; - - /** To enable to clear buffer once packet contains data from a different side than the side seen before - */ - bool enableBaseBufferClearCondition; - - /** - * A c'tor for this struct - * @param[in] removeConnInfo The flag indicating whether to remove the connection data after a connection is closed. The default is true - * @param[in] closedConnectionDelay How long the closed connections will not be cleaned up. The value is expressed in seconds. If it's set to 0 the default value will be used. The default is 5. - * @param[in] maxNumToClean The maximum number of items to be cleaned up per one call of purgeClosedConnections. If it's set to 0 the default value will be used. The default is 30. - * @param[in] maxOutOfOrderFragments The maximum number of unmatched fragments to keep per flow before missed fragments are considered lost. The default is unlimited. - * @param[in] enableBaseBufferClearCondition To enable to clear buffer once packet contains data from a different side than the side seen before - */ - explicit TcpReassemblyConfiguration(bool removeConnInfo = true, uint32_t closedConnectionDelay = 5, uint32_t maxNumToClean = 30, uint32_t maxOutOfOrderFragments = 0, - bool enableBaseBufferClearCondition = true) : removeConnInfo(removeConnInfo), closedConnectionDelay(closedConnectionDelay), maxNumToClean(maxNumToClean), maxOutOfOrderFragments(maxOutOfOrderFragments), enableBaseBufferClearCondition(enableBaseBufferClearCondition) - { - } -}; - - -/** - * @class TcpReassembly - * A class containing the TCP reassembly logic. Please refer to the documentation at the top of TcpReassembly.h for understanding how to use this class - */ -class TcpReassembly -{ -public: - - /** - * An enum for connection end reasons - */ - enum ConnectionEndReason - { - /** Connection ended because of FIN or RST packet */ - TcpReassemblyConnectionClosedByFIN_RST, - /** Connection ended manually by the user */ - TcpReassemblyConnectionClosedManually - }; - - /** - * An enum for providing reassembly status for each processed packet - */ - enum ReassemblyStatus - { - /** - * The processed packet contains valid TCP payload, and its payload is processed by `OnMessageReadyCallback` callback function. - * The packet may be: - * 1. An in-order TCP packet, meaning `packet_sequence == sequence_expected`. - * Note if there's any buffered out-of-order packet waiting for this packet, their associated callbacks are called in this `reassemblePacket` call. - * 2. An out-of-order TCP packet which satisfy `packet_sequence < sequence_expected && packet_sequence + packet_payload_length > sequence_expected`. - * Note only the new data (the `[sequence_expected, packet_sequence + packet_payload_length]` part ) is processed by `OnMessageReadyCallback` callback function. - */ - TcpMessageHandled, - /** - * The processed packet is an out-of-order TCP packet, meaning `packet_sequence > sequence_expected`. It's buffered so no `OnMessageReadyCallback` callback function is called. - * The callback function for this packet maybe called LATER, under different circumstances: - * 1. When an in-order packet which is right before this packet arrives(case 1 and case 2 described in `TcpMessageHandled` section above). - * 2. When a FIN or RST packet arrives, which will clear the buffered out-of-order packets of this side. - * If this packet contains "new data", meaning `(packet_sequence <= sequence_expected) && (packet_sequence + packet_payload_length > sequence_expected)`, the new data is processed by `OnMessageReadyCallback` callback. - */ - OutOfOrderTcpMessageBuffered, - /** - * The processed packet is a FIN or RST packet with no payload. - * Buffered out-of-order packets will be cleared. - * If they contain "new data", the new data is processed by `OnMessageReadyCallback` callback. - */ - FIN_RSTWithNoData, - /** - * The processed packet is not a SYN/SYNACK/FIN/RST packet and has no payload. - * Normally it's just a bare ACK packet. - * It's ignored and no callback function is called. - */ - Ignore_PacketWithNoData, - /** - * The processed packet comes from a closed flow(an in-order FIN or RST is seen). - * It's ignored and no callback function is called. - */ - Ignore_PacketOfClosedFlow, - /** - * The processed packet is a restransmission packet with no new data, meaning the `packet_sequence + packet_payload_length < sequence_expected`. - * It's ignored and no callback function is called. - */ - Ignore_Retransimission, - /** - * The processed packet is not an IP packet. - * It's ignored and no callback function is called. - */ - NonIpPacket, - /** - * The processed packet is not a TCP packet. - * It's ignored and no callback function is called. - */ - NonTcpPacket, - /** - * The processed packet does not belong to any known TCP connection. - * It's ignored and no callback function is called. - * Normally this will be happen. - */ - Error_PacketDoesNotMatchFlow, - }; - - /** - * The type for storing the connection information - */ - typedef std::map ConnectionInfoList; - - /** - * @typedef OnTcpMessageReady - * A callback invoked when new data arrives on a connection - * @param[in] side The side this data belongs to (MachineA->MachineB or vice versa). The value is 0 or 1 where 0 is the first side seen in the connection and 1 is the second side seen - * @param[in] tcpData The TCP data itself + connection information - * @param[in] userCookie A pointer to the cookie provided by the user in TcpReassembly c'tor (or NULL if no cookie provided) - */ - typedef void (*OnTcpMessageReady)(int8_t side, const TcpStreamData& tcpData, void* userCookie); - - /** - * @typedef OnTcpConnectionStart - * A callback invoked when a new TCP connection is identified (whether it begins with a SYN packet or not) - * @param[in] connectionData Connection information - * @param[in] userCookie A pointer to the cookie provided by the user in TcpReassembly c'tor (or NULL if no cookie provided) - */ - typedef void (*OnTcpConnectionStart)(const ConnectionData& connectionData, void* userCookie); - - /** - * @typedef OnTcpConnectionEnd - * A callback invoked when a TCP connection is terminated, either by a FIN or RST packet or manually by the user - * @param[in] connectionData Connection information - * @param[in] reason The reason for connection termination: FIN/RST packet or manually by the user - * @param[in] userCookie A pointer to the cookie provided by the user in TcpReassembly c'tor (or NULL if no cookie provided) - */ - typedef void (*OnTcpConnectionEnd)(const ConnectionData& connectionData, ConnectionEndReason reason, void* userCookie); - - /** - * A c'tor for this class - * @param[in] onMessageReadyCallback The callback to be invoked when new data arrives - * @param[in] userCookie A pointer to an object provided by the user. This pointer will be returned when invoking the various callbacks. This parameter is optional, default cookie is NULL - * @param[in] onConnectionStartCallback The callback to be invoked when a new connection is identified. This parameter is optional - * @param[in] onConnectionEndCallback The callback to be invoked when a new connection is terminated (either by a FIN/RST packet or manually by the user). This parameter is optional - * @param[in] config Optional parameter for defining special configuration parameters. If not set the default parameters will be set - */ - explicit TcpReassembly(OnTcpMessageReady onMessageReadyCallback, void* userCookie = NULL, OnTcpConnectionStart onConnectionStartCallback = NULL, OnTcpConnectionEnd onConnectionEndCallback = NULL, const TcpReassemblyConfiguration &config = TcpReassemblyConfiguration()); - - /** - * The most important method of this class which gets a packet from the user and processes it. If this packet opens a new connection, ends a connection or contains new data on an - * existing connection, the relevant callback will be called (TcpReassembly#OnTcpMessageReady, TcpReassembly#OnTcpConnectionStart, TcpReassembly#OnTcpConnectionEnd) - * @param[in] tcpData A reference to the packet to process - * @return A enum of `TcpReassembly::ReassemblyStatus`, indicating status of TCP reassembly - */ - ReassemblyStatus reassemblePacket(Packet& tcpData); - - /** - * The most important method of this class which gets a raw packet from the user and processes it. If this packet opens a new connection, ends a connection or contains new data on an - * existing connection, the relevant callback will be invoked (TcpReassembly#OnTcpMessageReady, TcpReassembly#OnTcpConnectionStart, TcpReassembly#OnTcpConnectionEnd) - * @param[in] tcpRawData A reference to the raw packet to process - * @return A enum of `TcpReassembly::ReassemblyStatus`, indicating status of TCP reassembly - */ - ReassemblyStatus reassemblePacket(RawPacket* tcpRawData); - - /** - * Close a connection manually. If the connection doesn't exist or already closed an error log is printed. This method will cause the TcpReassembly#OnTcpConnectionEnd to be invoked with - * a reason of TcpReassembly#TcpReassemblyConnectionClosedManually - * @param[in] flowKey A 4-byte hash key representing the connection. Can be taken from a ConnectionData instance - */ - void closeConnection(uint32_t flowKey); - - /** - * Close all open connections manually. This method will cause the TcpReassembly#OnTcpConnectionEnd to be invoked for each connection with a reason of - * TcpReassembly#TcpReassemblyConnectionClosedManually - */ - void closeAllConnections(); - - /** - * Get a map of all connections managed by this TcpReassembly instance (both connections that are open and those that are already closed) - * @return A map of all connections managed. Notice this map is constant and cannot be changed by the user - */ - const ConnectionInfoList& getConnectionInformation() const { return m_ConnectionInfo; } - - /** - * Check if a certain connection managed by this TcpReassembly instance is currently opened or closed - * @param[in] connection The connection to check - * @return A positive number (> 0) if connection is opened, zero (0) if connection is closed, and a negative number (< 0) if this connection isn't managed by this TcpReassembly instance - */ - int isConnectionOpen(const ConnectionData& connection) const; - - /** - * Clean up the closed connections from the memory - * @param[in] maxNumToClean The maximum number of items to be cleaned up per one call. This parameter, when its value is not zero, overrides the value that was set by the constructor. - * @return The number of cleared items - */ - uint32_t purgeClosedConnections(uint32_t maxNumToClean = 0); - -private: - struct TcpFragment - { - uint32_t sequence; - size_t dataLength; - uint8_t* data; - timeval timestamp; - - TcpFragment() : sequence(0), dataLength(0), data(NULL) {} - ~TcpFragment() { delete [] data; } - }; - - struct TcpOneSideData - { - IPAddress srcIP; - uint16_t srcPort; - uint32_t sequence; - PointerVector tcpFragmentList; - bool gotFinOrRst; - - TcpOneSideData() : srcPort(0), sequence(0), gotFinOrRst(false) {} - }; - - struct TcpReassemblyData - { - bool closed; - int8_t numOfSides; - int8_t prevSide; - TcpOneSideData twoSides[2]; - ConnectionData connData; - - TcpReassemblyData() : closed(false), numOfSides(0), prevSide(-1) {} - }; - - typedef std::map ConnectionList; - typedef std::map > CleanupList; - - OnTcpMessageReady m_OnMessageReadyCallback; - OnTcpConnectionStart m_OnConnStart; - OnTcpConnectionEnd m_OnConnEnd; - void* m_UserCookie; - ConnectionList m_ConnectionList; - ConnectionInfoList m_ConnectionInfo; - CleanupList m_CleanupList; - bool m_RemoveConnInfo; - uint32_t m_ClosedConnectionDelay; - uint32_t m_MaxNumToClean; - size_t m_MaxOutOfOrderFragments; - time_t m_PurgeTimepoint; - bool m_EnableBaseBufferClearCondition; - - void checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, bool cleanWholeFragList); - - void handleFinOrRst(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, uint32_t flowKey, bool isRst); - - void closeConnectionInternal(uint32_t flowKey, ConnectionEndReason reason); - - void insertIntoCleanupList(uint32_t flowKey); -}; - -} - -#endif /* PACKETPP_TCP_REASSEMBLY */ +#ifndef PACKETPP_TCP_REASSEMBLY +#define PACKETPP_TCP_REASSEMBLY + +#include "Packet.h" +#include "IpAddress.h" +#include "PointerVector.h" +#include +#include +#include + + +/** + * @file + * This is an implementation of TCP reassembly logic, which means reassembly of TCP messages spanning multiple TCP segments (or packets).
+ * This logic can be useful in analyzing messages for a large number of protocols implemented on top of TCP including HTTP, SSL/TLS, FTP and many many more. + * + * __General Features:__ + * - Manage multiple TCP connections under one pcpp#TcpReassembly instance + * - Support TCP retransmission + * - Support out-of-order packets + * - Support missing TCP data + * - TCP connections can end "naturally" (by FIN/RST packets) or manually by the user + * - Support callbacks for new TCP data, connection start and connection end + * + * __Logic Description:__ + * - The user creates an instance of the pcpp#TcpReassembly class + * - Then the user starts feeding it with TCP packets + * - The pcpp#TcpReassembly instance manages all TCP connections from the packets it's being fed. For each connection it manages its 2 sides (A->B and B->A) + * - When a packet arrives, it is first classified to a certain TCP connection + * - Then it is classified to a certain side of the TCP connection + * - Then the pcpp#TcpReassembly logic tries to understand if the data in this packet is the expected data (sequence-wise) and if it's new (e.g isn't a retransmission) + * - If the packet data matches these criteria a callback is being invoked. This callback is supplied by the user in the creation of the pcpp#TcpReassembly instance. This callback contains + * the new data (of course), but also information about the connection (5-tuple, 4-byte hash key describing the connection, etc.) and also a pointer to a "user cookie", meaning a pointer to + * a structure provided by the user during the creation of the pcpp#TcpReassembly instance + * - If the data in this packet isn't new, it's being ignored + * - If the data in this packet isn't expected (meaning this packet came out-of-order), then the data is being queued internally and will be sent to the user when its turn arrives + * (meaning, after the data before arrives) + * - If the missing data doesn't arrive until a new message from the other side of the connection arrives or until the connection ends - this will be considered as missing data and the + * queued data will be sent to the user, but the string "[X bytes missing]" will be added to the message sent in the callback + * - pcpp#TcpReassembly supports 2 more callbacks - one is invoked when a new TCP connection is first seen and the other when it's ended (either by a FIN/RST packet or manually by the user). + * Both of these callbacks contain data about the connection (5-tuple, 4-byte hash key describing the connection, etc.) and also a pointer to a "user cookie", meaning a pointer to a + * structure provided by the user during the creation of the pcpp#TcpReassembly instance. The end connection callback also provides the reason for closing it ("naturally" or manually) + * + * __Basic Usage and APIs:__ + * - pcpp#TcpReassembly c'tor - Create an instance, provide the callbacks and the user cookie to the instance + * - pcpp#TcpReassembly#reassemblePacket() - Feed pcpp#TcpReassembly instance with packets + * - pcpp#TcpReassembly#closeConnection() - Manually close a connection by a flow key + * - pcpp#TcpReassembly#closeAllConnections() - Manually close all currently opened connections + * - pcpp#TcpReassembly#OnTcpMessageReady callback - Invoked when new data arrives on a certain connection. Contains the new data as well as connection data (5-tuple, flow key) + * - pcpp#TcpReassembly#OnTcpConnectionStart callback - Invoked when a new connection is identified + * - pcpp#TcpReassembly#OnTcpConnectionEnd callback - Invoked when a connection ends (either by FIN/RST or manually by the user) + * + * __Additional information:__ + * When the connection is closed the information is not being deleted from memory immediately. There is a delay between these moments. Existence of this delay is caused by two reasons: + * - pcpp#TcpReassembly#reassemblePacket() should detect the packets that arrive after the FIN packet has been received + * - the user can use the information about connections managed by pcpp#TcpReassembly instance. Following methods are used for this purpose: pcpp#TcpReassembly#getConnectionInformation and pcpp#TcpReassembly#isConnectionOpen. + * Cleaning of memory can be performed automatically (the default behavior) by pcpp#TcpReassembly#reassemblePacket() or manually by calling pcpp#TcpReassembly#purgeClosedConnections in the user code. + * Automatic cleaning is performed once per second. + * + * The struct pcpp#TcpReassemblyConfiguration allows to setup the parameters of cleanup. Following parameters are supported: + * - pcpp#TcpReassemblyConfiguration#doNotRemoveConnInfo - if this member is set to false the automatic cleanup mode is applied + * - pcpp#TcpReassemblyConfiguration#closedConnectionDelay - the value of delay expressed in seconds. The minimum value is 1 + * - pcpp#TcpReassemblyConfiguration#maxNumToClean - to avoid performance overhead when the cleanup is being performed, this parameter is used. It defines the maximum number of items to be removed per one call of pcpp#TcpReassembly#purgeClosedConnections + * - pcpp#TcpReassemblyConfiguration#maxOutOfOrderFragments - the maximum number of unmatched fragments to keep per flow before missed fragments are considered lost. A value of 0 means unlimited + * + */ + +/** + * @namespace pcpp + * @brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + +/** + * @struct ConnectionData + * Represents basic TCP/UDP + IP connection data + */ +struct ConnectionData +{ + /** Source IP address */ + IPAddress srcIP; + /** Destination IP address */ + IPAddress dstIP; + /** Source TCP/UDP port */ + uint16_t srcPort; + /** Destination TCP/UDP port */ + uint16_t dstPort; + /** A 4-byte hash key representing the connection */ + uint32_t flowKey; + /** Start TimeStamp of the connection */ + timeval startTime; + /** End TimeStamp of the connection */ + timeval endTime; + + /** + * A c'tor for this struct that basically zeros all members + */ + ConnectionData() : srcPort(0), dstPort(0), flowKey(0), startTime(), endTime() {} + + /** + * Set startTime of Connection + * @param[in] startTime integer value + */ + void setStartTime(const timeval &startTime) { this->startTime = startTime; } + + /** + * Set endTime of Connection + * @param[in] endTime integer value + */ + void setEndTime(const timeval &endTime) { this->endTime = endTime; } +}; + + +class TcpReassembly; + + +/** + * @class TcpStreamData + * When following a TCP connection each packet may contain a piece of the data transferred between the client and the server. This class represents these pieces: each instance of it + * contains a piece of data, usually extracted from a single packet, as well as information about the connection + */ +class TcpStreamData +{ +public: + /** + * A c'tor for this class that get data from outside and set the internal members + * @param[in] tcpData A pointer to buffer containing the TCP data piece + * @param[in] tcpDataLength The length of the buffer + * @param[in] missingBytes The number of missing bytes due to packet loss. + * @param[in] connData TCP connection information for this TCP data + * @param[in] timestamp when this packet was received + */ + TcpStreamData(const uint8_t* tcpData, size_t tcpDataLength, size_t missingBytes, const ConnectionData& connData, timeval timestamp) + : m_Data(tcpData), m_DataLen(tcpDataLength), m_MissingBytes(missingBytes), m_Connection(connData), m_Timestamp(timestamp) + { + } + + /** + * A getter for the data buffer + * @return A pointer to the buffer + */ + const uint8_t* getData() const { return m_Data; } + + /** + * A getter for buffer length + * @return Buffer length + */ + size_t getDataLength() const { return m_DataLen; } + + /** + * A getter for missing byte count due to packet loss. + * @return Missing byte count + */ + size_t getMissingByteCount() const { return m_MissingBytes; } + + /** + * Determine if bytes are missing. getMissingByteCount can be called to determine the number of missing bytes. + * @return true if bytes are missing. + */ + bool isBytesMissing() const { return getMissingByteCount() > 0; } + + /** + * A getter for the connection data + * @return The const reference to connection data + */ + const ConnectionData& getConnectionData() const { return m_Connection; } + + /** + * A getter for the timestamp of this packet + * @return The const timeval object with timestamp of this packet + */ + timeval getTimeStamp() const { return m_Timestamp; } + +private: + const uint8_t* m_Data; + size_t m_DataLen; + size_t m_MissingBytes; + const ConnectionData& m_Connection; + timeval m_Timestamp; +}; + + +/** + * @struct TcpReassemblyConfiguration + * A structure for configuring the TcpReassembly class + */ +struct TcpReassemblyConfiguration +{ + /** The flag indicating whether to remove the connection data after a connection is closed */ + bool removeConnInfo; + + /** How long the closed connections will not be cleaned up. The value is expressed in seconds. If the value is set to 0 then TcpReassembly should use the default value. + * This parameter is only relevant if removeConnInfo is equal to true. + */ + uint32_t closedConnectionDelay; + + /** The maximum number of items to be cleaned up per one call of purgeClosedConnections. If the value is set to 0 then TcpReassembly should use the default value. + * This parameter is only relevant if removeConnInfo is equal to true. + */ + uint32_t maxNumToClean; + + /** The maximum number of fragments with a non-matching sequence-number to store per connection flow before packets are assumed permanently missed. + If the value is 0, TcpReassembly should keep out of order fragments indefinitely, or until a message from the paired side is seen. + */ + uint32_t maxOutOfOrderFragments; + + /** To enable to clear buffer once packet contains data from a different side than the side seen before + */ + bool enableBaseBufferClearCondition; + + /** + * A c'tor for this struct + * @param[in] removeConnInfo The flag indicating whether to remove the connection data after a connection is closed. The default is true + * @param[in] closedConnectionDelay How long the closed connections will not be cleaned up. The value is expressed in seconds. If it's set to 0 the default value will be used. The default is 5. + * @param[in] maxNumToClean The maximum number of items to be cleaned up per one call of purgeClosedConnections. If it's set to 0 the default value will be used. The default is 30. + * @param[in] maxOutOfOrderFragments The maximum number of unmatched fragments to keep per flow before missed fragments are considered lost. The default is unlimited. + * @param[in] enableBaseBufferClearCondition To enable to clear buffer once packet contains data from a different side than the side seen before + */ + explicit TcpReassemblyConfiguration(bool removeConnInfo = true, uint32_t closedConnectionDelay = 5, uint32_t maxNumToClean = 30, uint32_t maxOutOfOrderFragments = 0, + bool enableBaseBufferClearCondition = true) : removeConnInfo(removeConnInfo), closedConnectionDelay(closedConnectionDelay), maxNumToClean(maxNumToClean), maxOutOfOrderFragments(maxOutOfOrderFragments), enableBaseBufferClearCondition(enableBaseBufferClearCondition) + { + } +}; + + +/** + * @class TcpReassembly + * A class containing the TCP reassembly logic. Please refer to the documentation at the top of TcpReassembly.h for understanding how to use this class + */ +class TcpReassembly +{ +public: + + /** + * An enum for connection end reasons + */ + enum ConnectionEndReason + { + /** Connection ended because of FIN or RST packet */ + TcpReassemblyConnectionClosedByFIN_RST, + /** Connection ended manually by the user */ + TcpReassemblyConnectionClosedManually + }; + + /** + * An enum for providing reassembly status for each processed packet + */ + enum ReassemblyStatus + { + /** + * The processed packet contains valid TCP payload, and its payload is processed by `OnMessageReadyCallback` callback function. + * The packet may be: + * 1. An in-order TCP packet, meaning `packet_sequence == sequence_expected`. + * Note if there's any buffered out-of-order packet waiting for this packet, their associated callbacks are called in this `reassemblePacket` call. + * 2. An out-of-order TCP packet which satisfy `packet_sequence < sequence_expected && packet_sequence + packet_payload_length > sequence_expected`. + * Note only the new data (the `[sequence_expected, packet_sequence + packet_payload_length]` part ) is processed by `OnMessageReadyCallback` callback function. + */ + TcpMessageHandled, + /** + * The processed packet is an out-of-order TCP packet, meaning `packet_sequence > sequence_expected`. It's buffered so no `OnMessageReadyCallback` callback function is called. + * The callback function for this packet maybe called LATER, under different circumstances: + * 1. When an in-order packet which is right before this packet arrives(case 1 and case 2 described in `TcpMessageHandled` section above). + * 2. When a FIN or RST packet arrives, which will clear the buffered out-of-order packets of this side. + * If this packet contains "new data", meaning `(packet_sequence <= sequence_expected) && (packet_sequence + packet_payload_length > sequence_expected)`, the new data is processed by `OnMessageReadyCallback` callback. + */ + OutOfOrderTcpMessageBuffered, + /** + * The processed packet is a FIN or RST packet with no payload. + * Buffered out-of-order packets will be cleared. + * If they contain "new data", the new data is processed by `OnMessageReadyCallback` callback. + */ + FIN_RSTWithNoData, + /** + * The processed packet is not a SYN/SYNACK/FIN/RST packet and has no payload. + * Normally it's just a bare ACK packet. + * It's ignored and no callback function is called. + */ + Ignore_PacketWithNoData, + /** + * The processed packet comes from a closed flow(an in-order FIN or RST is seen). + * It's ignored and no callback function is called. + */ + Ignore_PacketOfClosedFlow, + /** + * The processed packet is a restransmission packet with no new data, meaning the `packet_sequence + packet_payload_length < sequence_expected`. + * It's ignored and no callback function is called. + */ + Ignore_Retransimission, + /** + * The processed packet is not an IP packet. + * It's ignored and no callback function is called. + */ + NonIpPacket, + /** + * The processed packet is not a TCP packet. + * It's ignored and no callback function is called. + */ + NonTcpPacket, + /** + * The processed packet does not belong to any known TCP connection. + * It's ignored and no callback function is called. + * Normally this will be happen. + */ + Error_PacketDoesNotMatchFlow, + }; + + /** + * The type for storing the connection information + */ + typedef std::map ConnectionInfoList; + + /** + * @typedef OnTcpMessageReady + * A callback invoked when new data arrives on a connection + * @param[in] side The side this data belongs to (MachineA->MachineB or vice versa). The value is 0 or 1 where 0 is the first side seen in the connection and 1 is the second side seen + * @param[in] tcpData The TCP data itself + connection information + * @param[in] userCookie A pointer to the cookie provided by the user in TcpReassembly c'tor (or NULL if no cookie provided) + */ + typedef void (*OnTcpMessageReady)(int8_t side, const TcpStreamData& tcpData, void* userCookie); + + /** + * @typedef OnTcpConnectionStart + * A callback invoked when a new TCP connection is identified (whether it begins with a SYN packet or not) + * @param[in] connectionData Connection information + * @param[in] userCookie A pointer to the cookie provided by the user in TcpReassembly c'tor (or NULL if no cookie provided) + */ + typedef void (*OnTcpConnectionStart)(const ConnectionData& connectionData, void* userCookie); + + /** + * @typedef OnTcpConnectionEnd + * A callback invoked when a TCP connection is terminated, either by a FIN or RST packet or manually by the user + * @param[in] connectionData Connection information + * @param[in] reason The reason for connection termination: FIN/RST packet or manually by the user + * @param[in] userCookie A pointer to the cookie provided by the user in TcpReassembly c'tor (or NULL if no cookie provided) + */ + typedef void (*OnTcpConnectionEnd)(const ConnectionData& connectionData, ConnectionEndReason reason, void* userCookie); + + /** + * A c'tor for this class + * @param[in] onMessageReadyCallback The callback to be invoked when new data arrives + * @param[in] userCookie A pointer to an object provided by the user. This pointer will be returned when invoking the various callbacks. This parameter is optional, default cookie is NULL + * @param[in] onConnectionStartCallback The callback to be invoked when a new connection is identified. This parameter is optional + * @param[in] onConnectionEndCallback The callback to be invoked when a new connection is terminated (either by a FIN/RST packet or manually by the user). This parameter is optional + * @param[in] config Optional parameter for defining special configuration parameters. If not set the default parameters will be set + */ + explicit TcpReassembly(OnTcpMessageReady onMessageReadyCallback, void* userCookie = NULL, OnTcpConnectionStart onConnectionStartCallback = NULL, OnTcpConnectionEnd onConnectionEndCallback = NULL, const TcpReassemblyConfiguration &config = TcpReassemblyConfiguration()); + + /** + * The most important method of this class which gets a packet from the user and processes it. If this packet opens a new connection, ends a connection or contains new data on an + * existing connection, the relevant callback will be called (TcpReassembly#OnTcpMessageReady, TcpReassembly#OnTcpConnectionStart, TcpReassembly#OnTcpConnectionEnd) + * @param[in] tcpData A reference to the packet to process + * @return A enum of `TcpReassembly::ReassemblyStatus`, indicating status of TCP reassembly + */ + ReassemblyStatus reassemblePacket(Packet& tcpData); + + /** + * The most important method of this class which gets a raw packet from the user and processes it. If this packet opens a new connection, ends a connection or contains new data on an + * existing connection, the relevant callback will be invoked (TcpReassembly#OnTcpMessageReady, TcpReassembly#OnTcpConnectionStart, TcpReassembly#OnTcpConnectionEnd) + * @param[in] tcpRawData A reference to the raw packet to process + * @return A enum of `TcpReassembly::ReassemblyStatus`, indicating status of TCP reassembly + */ + ReassemblyStatus reassemblePacket(RawPacket* tcpRawData); + + /** + * Close a connection manually. If the connection doesn't exist or already closed an error log is printed. This method will cause the TcpReassembly#OnTcpConnectionEnd to be invoked with + * a reason of TcpReassembly#TcpReassemblyConnectionClosedManually + * @param[in] flowKey A 4-byte hash key representing the connection. Can be taken from a ConnectionData instance + */ + void closeConnection(uint32_t flowKey); + + /** + * Close all open connections manually. This method will cause the TcpReassembly#OnTcpConnectionEnd to be invoked for each connection with a reason of + * TcpReassembly#TcpReassemblyConnectionClosedManually + */ + void closeAllConnections(); + + /** + * Get a map of all connections managed by this TcpReassembly instance (both connections that are open and those that are already closed) + * @return A map of all connections managed. Notice this map is constant and cannot be changed by the user + */ + const ConnectionInfoList& getConnectionInformation() const { return m_ConnectionInfo; } + + /** + * Check if a certain connection managed by this TcpReassembly instance is currently opened or closed + * @param[in] connection The connection to check + * @return A positive number (> 0) if connection is opened, zero (0) if connection is closed, and a negative number (< 0) if this connection isn't managed by this TcpReassembly instance + */ + int isConnectionOpen(const ConnectionData& connection) const; + + /** + * Clean up the closed connections from the memory + * @param[in] maxNumToClean The maximum number of items to be cleaned up per one call. This parameter, when its value is not zero, overrides the value that was set by the constructor. + * @return The number of cleared items + */ + uint32_t purgeClosedConnections(uint32_t maxNumToClean = 0); + +private: + struct TcpFragment + { + uint32_t sequence; + size_t dataLength; + uint8_t* data; + timeval timestamp; + + TcpFragment() : sequence(0), dataLength(0), data(NULL) {} + ~TcpFragment() { delete [] data; } + }; + + struct TcpOneSideData + { + IPAddress srcIP; + uint16_t srcPort; + uint32_t sequence; + PointerVector tcpFragmentList; + bool gotFinOrRst; + + TcpOneSideData() : srcPort(0), sequence(0), gotFinOrRst(false) {} + }; + + struct TcpReassemblyData + { + bool closed; + int8_t numOfSides; + int8_t prevSide; + TcpOneSideData twoSides[2]; + ConnectionData connData; + + TcpReassemblyData() : closed(false), numOfSides(0), prevSide(-1) {} + }; + + typedef std::map ConnectionList; + typedef std::map > CleanupList; + + OnTcpMessageReady m_OnMessageReadyCallback; + OnTcpConnectionStart m_OnConnStart; + OnTcpConnectionEnd m_OnConnEnd; + void* m_UserCookie; + ConnectionList m_ConnectionList; + ConnectionInfoList m_ConnectionInfo; + CleanupList m_CleanupList; + bool m_RemoveConnInfo; + uint32_t m_ClosedConnectionDelay; + uint32_t m_MaxNumToClean; + size_t m_MaxOutOfOrderFragments; + time_t m_PurgeTimepoint; + bool m_EnableBaseBufferClearCondition; + + void checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, bool cleanWholeFragList); + + void handleFinOrRst(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, uint32_t flowKey, bool isRst); + + void closeConnectionInternal(uint32_t flowKey, ConnectionEndReason reason); + + void insertIntoCleanupList(uint32_t flowKey); +}; + +} + +#endif /* PACKETPP_TCP_REASSEMBLY */ diff --git a/Packet++/src/DhcpLayer.cpp b/Packet++/src/DhcpLayer.cpp index d65db28d23..c75c370a08 100644 --- a/Packet++/src/DhcpLayer.cpp +++ b/Packet++/src/DhcpLayer.cpp @@ -1,314 +1,314 @@ -#define LOG_MODULE PacketLogModuleDhcpLayer - -#include "DhcpLayer.h" -#include "Logger.h" - -namespace pcpp -{ - -#define DHCP_MAGIC_NUMBER 0x63538263 - - -DhcpOption DhcpOptionBuilder::build() const -{ - size_t recSize = 2 * sizeof(uint8_t) + m_RecValueLen; - uint8_t recType = static_cast(m_RecType); - - if ((recType == DHCPOPT_END || recType == DHCPOPT_PAD)) - { - if (m_RecValueLen != 0) - { - PCPP_LOG_ERROR("Can't set DHCP END option or DHCP PAD option with size different than 0, tried to set size " << (int)m_RecValueLen); - return DhcpOption(nullptr); - } - - recSize = sizeof(uint8_t); - } - - uint8_t* recordBuffer = new uint8_t[recSize]; - memset(recordBuffer, 0, recSize); - recordBuffer[0] = recType; - if (recSize > 1) - { - recordBuffer[1] = static_cast(m_RecValueLen); - if (m_RecValue != nullptr) - memcpy(recordBuffer+2, m_RecValue, m_RecValueLen); - else - memset(recordBuffer+2, 0, m_RecValueLen); - } - - return DhcpOption(recordBuffer); -} - -DhcpLayer::DhcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) -{ - m_Protocol = DHCP; -} - -void DhcpLayer::initDhcpLayer(size_t numOfBytesToAllocate) -{ - m_DataLen = numOfBytesToAllocate; - m_Data = new uint8_t[numOfBytesToAllocate]; - memset(m_Data, 0, numOfBytesToAllocate); - m_Protocol = DHCP; -} - -DhcpLayer::DhcpLayer() : Layer() -{ - initDhcpLayer(sizeof(dhcp_header)); -} - -DhcpLayer::DhcpLayer(DhcpMessageType msgType, const MacAddress& clientMacAddr) : Layer() -{ - initDhcpLayer(sizeof(dhcp_header) + 4*sizeof(uint8_t)); - - setClientHardwareAddress(clientMacAddr); - - uint8_t* msgTypeOptionPtr = m_Data + sizeof(dhcp_header); - msgTypeOptionPtr[0] = (uint8_t)DHCPOPT_DHCP_MESSAGE_TYPE; // option code - msgTypeOptionPtr[1] = 1; // option len - msgTypeOptionPtr[2] = (uint8_t)msgType; // option data - message type - - msgTypeOptionPtr[3] = (uint8_t)DHCPOPT_END; -} - -MacAddress DhcpLayer::getClientHardwareAddress() const -{ - dhcp_header* hdr = getDhcpHeader(); - if (hdr != nullptr && hdr->hardwareType == 1 && hdr->hardwareAddressLength == 6) - return MacAddress(hdr->clientHardwareAddress); - - PCPP_LOG_DEBUG("Hardware type isn't Ethernet or hardware addr len != 6, returning MacAddress:Zero"); - - return MacAddress::Zero; -} - -void DhcpLayer::setClientHardwareAddress(const MacAddress& addr) -{ - dhcp_header* hdr = getDhcpHeader(); - hdr->hardwareType = 1; // Ethernet - hdr->hardwareAddressLength = 6; // MAC address length - addr.copyTo(hdr->clientHardwareAddress); -} - -void DhcpLayer::computeCalculateFields() -{ - dhcp_header* hdr = getDhcpHeader(); - - hdr->magicNumber = DHCP_MAGIC_NUMBER; - - DhcpMessageType msgType = getMessageType(); - switch(msgType) - { - case DHCP_DISCOVER: - case DHCP_REQUEST: - case DHCP_DECLINE: - case DHCP_RELEASE: - case DHCP_INFORM: - case DHCP_UNKNOWN_MSG_TYPE: - hdr->opCode = DHCP_BOOTREQUEST; - break; - case DHCP_OFFER: - case DHCP_ACK: - case DHCP_NAK: - hdr->opCode = DHCP_BOOTREPLY; - break; - default: - break; - } - - hdr->hardwareType = 1; //Ethernet - hdr->hardwareAddressLength = 6; // MAC address length -} - -std::string DhcpLayer::toString() const -{ - std::string msgType = "Unknown"; - switch (getMessageType()) - { - case DHCP_DISCOVER: - { - msgType = "Discover"; - break; - } - case DHCP_OFFER: - { - msgType = "Offer"; - break; - } - case DHCP_REQUEST: - { - msgType = "Request"; - break; - } - case DHCP_DECLINE: - { - msgType = "Decline"; - break; - } - case DHCP_ACK: - { - msgType = "Acknowledge"; - break; - } - case DHCP_NAK: - { - msgType = "Negative Acknowledge"; - break; - } - case DHCP_RELEASE: - { - msgType = "Release"; - break; - } - case DHCP_INFORM: - { - msgType = "Inform"; - break; - } - default: - break; - - } - - return "DHCP layer (" + msgType + ")"; -} - -DhcpMessageType DhcpLayer::getMessageType() const -{ - DhcpOption opt = getOptionData(DHCPOPT_DHCP_MESSAGE_TYPE); - if (opt.isNull()) - return DHCP_UNKNOWN_MSG_TYPE; - - return (DhcpMessageType)opt.getValueAs(); -} - -bool DhcpLayer::setMessageType(DhcpMessageType msgType) -{ - if (msgType == DHCP_UNKNOWN_MSG_TYPE) - return false; - - DhcpOption opt = getOptionData(DHCPOPT_DHCP_MESSAGE_TYPE); - if (opt.isNull()) - { - opt = addOptionAfter(DhcpOptionBuilder(DHCPOPT_DHCP_MESSAGE_TYPE, (uint8_t)msgType), DHCPOPT_UNKNOWN); - if (opt.isNull()) - return false; - } - - opt.setValue((uint8_t)msgType); - return true; -} - -DhcpOption DhcpLayer::getOptionData(DhcpOptionTypes option) const -{ - return m_OptionReader.getTLVRecord((uint8_t)option, getOptionsBasePtr(), getHeaderLen() - sizeof(dhcp_header)); -} - -DhcpOption DhcpLayer::getFirstOptionData() const -{ - return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), getHeaderLen() - sizeof(dhcp_header)); -} - -DhcpOption DhcpLayer::getNextOptionData(DhcpOption dhcpOption) const -{ - return m_OptionReader.getNextTLVRecord(dhcpOption, getOptionsBasePtr(), getHeaderLen() - sizeof(dhcp_header)); -} - -size_t DhcpLayer::getOptionsCount() const -{ - return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), getHeaderLen() - sizeof(dhcp_header)); -} - -DhcpOption DhcpLayer::addOptionAt(const DhcpOptionBuilder& optionBuilder, int offset) -{ - DhcpOption newOpt = optionBuilder.build(); - - if (newOpt.isNull()) - { - PCPP_LOG_ERROR("Cannot build new option of type " << (int)newOpt.getType()); - return DhcpOption(nullptr); - } - - size_t sizeToExtend = newOpt.getTotalSize(); - - if (!extendLayer(offset, sizeToExtend)) - { - PCPP_LOG_ERROR("Could not extend DhcpLayer in [" << newOpt.getTotalSize() << "] bytes"); - newOpt.purgeRecordData(); - return DhcpOption(nullptr); - } - - memcpy(m_Data + offset, newOpt.getRecordBasePtr(), newOpt.getTotalSize()); - - uint8_t* newOptPtr = m_Data + offset; - - m_OptionReader.changeTLVRecordCount(1); - - newOpt.purgeRecordData(); - - return DhcpOption(newOptPtr); -} - -DhcpOption DhcpLayer::addOption(const DhcpOptionBuilder& optionBuilder) -{ - int offset = 0; - DhcpOption endOpt = getOptionData(DHCPOPT_END); - if (!endOpt.isNull()) - offset = endOpt.getRecordBasePtr() - m_Data; - else - offset = getHeaderLen(); - - return addOptionAt(optionBuilder, offset); -} - -DhcpOption DhcpLayer::addOptionAfter(const DhcpOptionBuilder& optionBuilder, DhcpOptionTypes prevOption) -{ - int offset = 0; - - DhcpOption prevOpt = getOptionData(prevOption); - - if (prevOpt.isNull()) - { - offset = sizeof(dhcp_header); - } - else - { - offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; - } - - return addOptionAt(optionBuilder, offset); -} - -bool DhcpLayer::removeOption(DhcpOptionTypes optionType) -{ - DhcpOption optToRemove = getOptionData(optionType); - if (optToRemove.isNull()) - { - return false; - } - - int offset = optToRemove.getRecordBasePtr() - m_Data; - - if (!shortenLayer(offset, optToRemove.getTotalSize())) - { - return false; - } - - m_OptionReader.changeTLVRecordCount(-1); - return true; -} - -bool DhcpLayer::removeAllOptions() -{ - int offset = sizeof(dhcp_header); - - if (!shortenLayer(offset, getHeaderLen()-offset)) - return false; - - m_OptionReader.changeTLVRecordCount(0-getOptionsCount()); - return true; -} - - -} +#define LOG_MODULE PacketLogModuleDhcpLayer + +#include "DhcpLayer.h" +#include "Logger.h" + +namespace pcpp +{ + +#define DHCP_MAGIC_NUMBER 0x63538263 + + +DhcpOption DhcpOptionBuilder::build() const +{ + size_t recSize = 2 * sizeof(uint8_t) + m_RecValueLen; + uint8_t recType = static_cast(m_RecType); + + if ((recType == DHCPOPT_END || recType == DHCPOPT_PAD)) + { + if (m_RecValueLen != 0) + { + PCPP_LOG_ERROR("Can't set DHCP END option or DHCP PAD option with size different than 0, tried to set size " << (int)m_RecValueLen); + return DhcpOption(nullptr); + } + + recSize = sizeof(uint8_t); + } + + uint8_t* recordBuffer = new uint8_t[recSize]; + memset(recordBuffer, 0, recSize); + recordBuffer[0] = recType; + if (recSize > 1) + { + recordBuffer[1] = static_cast(m_RecValueLen); + if (m_RecValue != nullptr) + memcpy(recordBuffer+2, m_RecValue, m_RecValueLen); + else + memset(recordBuffer+2, 0, m_RecValueLen); + } + + return DhcpOption(recordBuffer); +} + +DhcpLayer::DhcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) +{ + m_Protocol = DHCP; +} + +void DhcpLayer::initDhcpLayer(size_t numOfBytesToAllocate) +{ + m_DataLen = numOfBytesToAllocate; + m_Data = new uint8_t[numOfBytesToAllocate]; + memset(m_Data, 0, numOfBytesToAllocate); + m_Protocol = DHCP; +} + +DhcpLayer::DhcpLayer() : Layer() +{ + initDhcpLayer(sizeof(dhcp_header)); +} + +DhcpLayer::DhcpLayer(DhcpMessageType msgType, const MacAddress& clientMacAddr) : Layer() +{ + initDhcpLayer(sizeof(dhcp_header) + 4*sizeof(uint8_t)); + + setClientHardwareAddress(clientMacAddr); + + uint8_t* msgTypeOptionPtr = m_Data + sizeof(dhcp_header); + msgTypeOptionPtr[0] = (uint8_t)DHCPOPT_DHCP_MESSAGE_TYPE; // option code + msgTypeOptionPtr[1] = 1; // option len + msgTypeOptionPtr[2] = (uint8_t)msgType; // option data - message type + + msgTypeOptionPtr[3] = (uint8_t)DHCPOPT_END; +} + +MacAddress DhcpLayer::getClientHardwareAddress() const +{ + dhcp_header* hdr = getDhcpHeader(); + if (hdr != nullptr && hdr->hardwareType == 1 && hdr->hardwareAddressLength == 6) + return MacAddress(hdr->clientHardwareAddress); + + PCPP_LOG_DEBUG("Hardware type isn't Ethernet or hardware addr len != 6, returning MacAddress:Zero"); + + return MacAddress::Zero; +} + +void DhcpLayer::setClientHardwareAddress(const MacAddress& addr) +{ + dhcp_header* hdr = getDhcpHeader(); + hdr->hardwareType = 1; // Ethernet + hdr->hardwareAddressLength = 6; // MAC address length + addr.copyTo(hdr->clientHardwareAddress); +} + +void DhcpLayer::computeCalculateFields() +{ + dhcp_header* hdr = getDhcpHeader(); + + hdr->magicNumber = DHCP_MAGIC_NUMBER; + + DhcpMessageType msgType = getMessageType(); + switch(msgType) + { + case DHCP_DISCOVER: + case DHCP_REQUEST: + case DHCP_DECLINE: + case DHCP_RELEASE: + case DHCP_INFORM: + case DHCP_UNKNOWN_MSG_TYPE: + hdr->opCode = DHCP_BOOTREQUEST; + break; + case DHCP_OFFER: + case DHCP_ACK: + case DHCP_NAK: + hdr->opCode = DHCP_BOOTREPLY; + break; + default: + break; + } + + hdr->hardwareType = 1; //Ethernet + hdr->hardwareAddressLength = 6; // MAC address length +} + +std::string DhcpLayer::toString() const +{ + std::string msgType = "Unknown"; + switch (getMessageType()) + { + case DHCP_DISCOVER: + { + msgType = "Discover"; + break; + } + case DHCP_OFFER: + { + msgType = "Offer"; + break; + } + case DHCP_REQUEST: + { + msgType = "Request"; + break; + } + case DHCP_DECLINE: + { + msgType = "Decline"; + break; + } + case DHCP_ACK: + { + msgType = "Acknowledge"; + break; + } + case DHCP_NAK: + { + msgType = "Negative Acknowledge"; + break; + } + case DHCP_RELEASE: + { + msgType = "Release"; + break; + } + case DHCP_INFORM: + { + msgType = "Inform"; + break; + } + default: + break; + + } + + return "DHCP layer (" + msgType + ")"; +} + +DhcpMessageType DhcpLayer::getMessageType() const +{ + DhcpOption opt = getOptionData(DHCPOPT_DHCP_MESSAGE_TYPE); + if (opt.isNull()) + return DHCP_UNKNOWN_MSG_TYPE; + + return (DhcpMessageType)opt.getValueAs(); +} + +bool DhcpLayer::setMessageType(DhcpMessageType msgType) +{ + if (msgType == DHCP_UNKNOWN_MSG_TYPE) + return false; + + DhcpOption opt = getOptionData(DHCPOPT_DHCP_MESSAGE_TYPE); + if (opt.isNull()) + { + opt = addOptionAfter(DhcpOptionBuilder(DHCPOPT_DHCP_MESSAGE_TYPE, (uint8_t)msgType), DHCPOPT_UNKNOWN); + if (opt.isNull()) + return false; + } + + opt.setValue((uint8_t)msgType); + return true; +} + +DhcpOption DhcpLayer::getOptionData(DhcpOptionTypes option) const +{ + return m_OptionReader.getTLVRecord((uint8_t)option, getOptionsBasePtr(), getHeaderLen() - sizeof(dhcp_header)); +} + +DhcpOption DhcpLayer::getFirstOptionData() const +{ + return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), getHeaderLen() - sizeof(dhcp_header)); +} + +DhcpOption DhcpLayer::getNextOptionData(DhcpOption dhcpOption) const +{ + return m_OptionReader.getNextTLVRecord(dhcpOption, getOptionsBasePtr(), getHeaderLen() - sizeof(dhcp_header)); +} + +size_t DhcpLayer::getOptionsCount() const +{ + return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), getHeaderLen() - sizeof(dhcp_header)); +} + +DhcpOption DhcpLayer::addOptionAt(const DhcpOptionBuilder& optionBuilder, int offset) +{ + DhcpOption newOpt = optionBuilder.build(); + + if (newOpt.isNull()) + { + PCPP_LOG_ERROR("Cannot build new option of type " << (int)newOpt.getType()); + return DhcpOption(nullptr); + } + + size_t sizeToExtend = newOpt.getTotalSize(); + + if (!extendLayer(offset, sizeToExtend)) + { + PCPP_LOG_ERROR("Could not extend DhcpLayer in [" << newOpt.getTotalSize() << "] bytes"); + newOpt.purgeRecordData(); + return DhcpOption(nullptr); + } + + memcpy(m_Data + offset, newOpt.getRecordBasePtr(), newOpt.getTotalSize()); + + uint8_t* newOptPtr = m_Data + offset; + + m_OptionReader.changeTLVRecordCount(1); + + newOpt.purgeRecordData(); + + return DhcpOption(newOptPtr); +} + +DhcpOption DhcpLayer::addOption(const DhcpOptionBuilder& optionBuilder) +{ + int offset = 0; + DhcpOption endOpt = getOptionData(DHCPOPT_END); + if (!endOpt.isNull()) + offset = endOpt.getRecordBasePtr() - m_Data; + else + offset = getHeaderLen(); + + return addOptionAt(optionBuilder, offset); +} + +DhcpOption DhcpLayer::addOptionAfter(const DhcpOptionBuilder& optionBuilder, DhcpOptionTypes prevOption) +{ + int offset = 0; + + DhcpOption prevOpt = getOptionData(prevOption); + + if (prevOpt.isNull()) + { + offset = sizeof(dhcp_header); + } + else + { + offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; + } + + return addOptionAt(optionBuilder, offset); +} + +bool DhcpLayer::removeOption(DhcpOptionTypes optionType) +{ + DhcpOption optToRemove = getOptionData(optionType); + if (optToRemove.isNull()) + { + return false; + } + + int offset = optToRemove.getRecordBasePtr() - m_Data; + + if (!shortenLayer(offset, optToRemove.getTotalSize())) + { + return false; + } + + m_OptionReader.changeTLVRecordCount(-1); + return true; +} + +bool DhcpLayer::removeAllOptions() +{ + int offset = sizeof(dhcp_header); + + if (!shortenLayer(offset, getHeaderLen()-offset)) + return false; + + m_OptionReader.changeTLVRecordCount(0-getOptionsCount()); + return true; +} + + +} diff --git a/Packet++/src/DnsLayer.cpp b/Packet++/src/DnsLayer.cpp index 437d64c40e..644ea89b7b 100644 --- a/Packet++/src/DnsLayer.cpp +++ b/Packet++/src/DnsLayer.cpp @@ -1,878 +1,878 @@ -#define LOG_MODULE PacketLogModuleDnsLayer - -#include "DnsLayer.h" -#include "Logger.h" -#include "IpAddress.h" -#include -#include -#include -#include -#include "EndianPortable.h" - -namespace pcpp -{ - -// ~~~~~~~~ -// DnsLayer -// ~~~~~~~~ - -DnsLayer::DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : Layer(data, dataLen, prevLayer, packet) -{ - init(0, true); -} - -DnsLayer::DnsLayer() -{ - initNewLayer(0); -} - -DnsLayer::DnsLayer(const DnsLayer& other) : Layer(other) -{ - init(other.m_OffsetAdjustment, true); -} - -DnsLayer::DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, size_t offsetAdjustment) - : Layer(data, dataLen, prevLayer, packet) -{ - init(offsetAdjustment, true); -} - -DnsLayer::DnsLayer(size_t offsetAdjustment) -{ - initNewLayer(offsetAdjustment); -} - -DnsLayer& DnsLayer::operator=(const DnsLayer& other) -{ - Layer::operator=(other); - - IDnsResource* curResource = m_ResourceList; - while (curResource != nullptr) - { - IDnsResource* temp = curResource->getNextResource(); - delete curResource; - curResource = temp; - } - - init(other.m_OffsetAdjustment, true); - - return (*this); -} - -DnsLayer::~DnsLayer() -{ - IDnsResource* curResource = m_ResourceList; - while (curResource != nullptr) - { - IDnsResource* nextResource = curResource->getNextResource(); - delete curResource; - curResource = nextResource; - } -} - -void DnsLayer::init(size_t offsetAdjustment, bool callParseResource) -{ - m_OffsetAdjustment = offsetAdjustment; - m_Protocol = DNS; - m_ResourceList = nullptr; - - m_FirstQuery = nullptr; - m_FirstAnswer = nullptr; - m_FirstAuthority = nullptr; - m_FirstAdditional = nullptr; - - if (callParseResource) - parseResources(); -} - - -void DnsLayer::initNewLayer(size_t offsetAdjustment) -{ - m_OffsetAdjustment = offsetAdjustment; - const size_t headerLen = getBasicHeaderSize(); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - - init(m_OffsetAdjustment, false); -} - -size_t DnsLayer::getBasicHeaderSize() -{ - return sizeof(dnshdr) + m_OffsetAdjustment; -} - -dnshdr* DnsLayer::getDnsHeader() const -{ - uint8_t* ptr = m_Data + m_OffsetAdjustment; - return (dnshdr*)ptr; -} - -bool DnsLayer::extendLayer(int offsetInLayer, size_t numOfBytesToExtend, IDnsResource* resource) -{ - if (!Layer::extendLayer(offsetInLayer, numOfBytesToExtend)) - return false; - - IDnsResource* curResource = resource->getNextResource(); - while (curResource != nullptr) - { - curResource->m_OffsetInLayer += numOfBytesToExtend; - curResource = curResource->getNextResource(); - } - return true; -} - - -bool DnsLayer::shortenLayer(int offsetInLayer, size_t numOfBytesToShorten, IDnsResource* resource) -{ - if (!Layer::shortenLayer(offsetInLayer, numOfBytesToShorten)) - return false; - - IDnsResource* curResource = resource->getNextResource(); - while (curResource != nullptr) - { - curResource->m_OffsetInLayer -= numOfBytesToShorten; - curResource = curResource->getNextResource(); - } - return true; -} - - -void DnsLayer::parseResources() -{ - size_t offsetInPacket = getBasicHeaderSize(); - IDnsResource* curResource = m_ResourceList; - - uint16_t numOfQuestions = be16toh(getDnsHeader()->numberOfQuestions); - uint16_t numOfAnswers = be16toh(getDnsHeader()->numberOfAnswers); - uint16_t numOfAuthority = be16toh(getDnsHeader()->numberOfAuthority); - uint16_t numOfAdditional = be16toh(getDnsHeader()->numberOfAdditional); - - uint32_t numOfOtherResources = numOfQuestions + numOfAnswers + numOfAuthority + numOfAdditional; - - if (numOfOtherResources > 300) - { - PCPP_LOG_ERROR("DNS layer contains more than 300 resources, probably a bad packet. " - "Skipping parsing DNS resources"); - return; - } - - for (uint32_t i = 0; i < numOfOtherResources; i++) - { - DnsResourceType resType; - if (numOfQuestions > 0) - { - resType = DnsQueryType; - numOfQuestions--; - } - else if (numOfAnswers > 0) - { - resType = DnsAnswerType; - numOfAnswers--; - } - else if (numOfAuthority > 0) - { - resType = DnsAuthorityType; - numOfAuthority--; - } - else - { - resType = DnsAdditionalType; - numOfAdditional--; - } - - DnsResource* newResource = nullptr; - DnsQuery* newQuery = nullptr; - IDnsResource* newGenResource = nullptr; - if (resType == DnsQueryType) - { - newQuery = new DnsQuery(this, offsetInPacket); - newGenResource = newQuery; - offsetInPacket += newQuery->getSize(); - } - else - { - newResource = new DnsResource(this, offsetInPacket, resType); - newGenResource = newResource; - offsetInPacket += newResource->getSize(); - } - - if (offsetInPacket > m_DataLen) - { - //Parse packet failed, DNS resource is out of bounds. Probably a bad packet - delete newGenResource; - return; - } - - // this resource is the first resource - if (m_ResourceList == nullptr) - { - m_ResourceList = newGenResource; - curResource = m_ResourceList; - } - else - { - curResource->setNexResource(newGenResource); - curResource = curResource->getNextResource(); - } - - if (resType == DnsQueryType && m_FirstQuery == nullptr) - m_FirstQuery = newQuery; - else if (resType == DnsAnswerType && m_FirstAnswer == nullptr) - m_FirstAnswer = newResource; - else if (resType == DnsAuthorityType && m_FirstAuthority == nullptr) - m_FirstAuthority = newResource; - else if (resType == DnsAdditionalType && m_FirstAdditional == nullptr) - m_FirstAdditional = newResource; - } - -} - -IDnsResource* DnsLayer::getResourceByName(IDnsResource* startFrom, size_t resourceCount, const std::string& name, bool exactMatch) const -{ - size_t index = 0; - while (index < resourceCount) - { - if (startFrom == nullptr) - return nullptr; - - std::string resourceName = startFrom->getName(); - if (exactMatch && resourceName == name) - return startFrom; - else if (!exactMatch && resourceName.find(name) != std::string::npos) - return startFrom; - - startFrom = startFrom->getNextResource(); - - index++; - } - - return nullptr; -} - -DnsQuery* DnsLayer::getQuery(const std::string& name, bool exactMatch) const -{ - uint16_t numOfQueries = be16toh(getDnsHeader()->numberOfQuestions); - IDnsResource* res = getResourceByName(m_FirstQuery, numOfQueries, name, exactMatch); - if (res != nullptr) - return dynamic_cast(res); - return nullptr; -} - - -DnsQuery* DnsLayer::getFirstQuery() const -{ - return m_FirstQuery; -} - - -DnsQuery* DnsLayer::getNextQuery(DnsQuery* query) const -{ - if (query == nullptr - || query->getNextResource() == nullptr - || query->getType() != DnsQueryType - || query->getNextResource()->getType() != DnsQueryType) - return nullptr; - - return (DnsQuery*)(query->getNextResource()); -} - -size_t DnsLayer::getQueryCount() const -{ - return be16toh(getDnsHeader()->numberOfQuestions); -} - -DnsResource* DnsLayer::getAnswer(const std::string& name, bool exactMatch) const -{ - uint16_t numOfAnswers = be16toh(getDnsHeader()->numberOfAnswers); - IDnsResource* res = getResourceByName(m_FirstAnswer, numOfAnswers, name, exactMatch); - if (res != nullptr) - return dynamic_cast(res); - return nullptr; -} - -DnsResource* DnsLayer::getFirstAnswer() const -{ - return m_FirstAnswer; -} - -DnsResource* DnsLayer::getNextAnswer(DnsResource* answer) const -{ - if (answer == nullptr - || answer->getNextResource() == nullptr - || answer->getType() != DnsAnswerType - || answer->getNextResource()->getType() != DnsAnswerType) - return nullptr; - - return (DnsResource*)(answer->getNextResource()); -} - -size_t DnsLayer::getAnswerCount() const -{ - return be16toh(getDnsHeader()->numberOfAnswers); -} - -DnsResource* DnsLayer::getAuthority(const std::string& name, bool exactMatch) const -{ - uint16_t numOfAuthorities = be16toh(getDnsHeader()->numberOfAuthority); - IDnsResource* res = getResourceByName(m_FirstAuthority, numOfAuthorities, name, exactMatch); - if (res != nullptr) - return dynamic_cast(res); - return nullptr; -} - -DnsResource* DnsLayer::getFirstAuthority() const -{ - return m_FirstAuthority; -} - -DnsResource* DnsLayer::getNextAuthority(DnsResource* authority) const -{ - if (authority == nullptr - || authority->getNextResource() == nullptr - || authority->getType() != DnsAuthorityType - || authority->getNextResource()->getType() != DnsAuthorityType) - return nullptr; - - return (DnsResource*)(authority->getNextResource()); -} - -size_t DnsLayer::getAuthorityCount() const -{ - return be16toh(getDnsHeader()->numberOfAuthority); -} - -DnsResource* DnsLayer::getAdditionalRecord(const std::string& name, bool exactMatch) const -{ - uint16_t numOfAdditionalRecords = be16toh(getDnsHeader()->numberOfAdditional); - IDnsResource* res = getResourceByName(m_FirstAdditional, numOfAdditionalRecords, name, exactMatch); - if (res != nullptr) - return dynamic_cast(res); - return nullptr; -} - -DnsResource* DnsLayer::getFirstAdditionalRecord() const -{ - return m_FirstAdditional; -} - -DnsResource* DnsLayer::getNextAdditionalRecord(DnsResource* additionalRecord) const -{ - if (additionalRecord == nullptr - || additionalRecord->getNextResource() == nullptr - || additionalRecord->getType() != DnsAdditionalType - || additionalRecord->getNextResource()->getType() != DnsAdditionalType) - return nullptr; - - return (DnsResource*)(additionalRecord->getNextResource()); -} - -size_t DnsLayer::getAdditionalRecordCount() const -{ - return be16toh(getDnsHeader()->numberOfAdditional); -} - -std::string DnsLayer::toString() const -{ - std::ostringstream tidAsString; - tidAsString << be16toh(getDnsHeader()->transactionID); - - std::ostringstream queryCount; - queryCount << getQueryCount(); - - std::ostringstream answerCount; - answerCount << getAnswerCount(); - - std::ostringstream authorityCount; - authorityCount << getAuthorityCount(); - - std::ostringstream additionalCount; - additionalCount << getAdditionalRecordCount(); - - if (getDnsHeader()->queryOrResponse == 1) - { - return "DNS query response, ID: " + tidAsString.str() + ";" + - " queries: " + queryCount.str() + - ", answers: " + answerCount.str() + - ", authorities: " + authorityCount.str() + - ", additional record: " + additionalCount.str(); - } - else if (getDnsHeader()->queryOrResponse == 0) - { - return "DNS query, ID: " + tidAsString.str() + ";" + - " queries: " + queryCount.str() + - ", answers: " + answerCount.str() + - ", authorities: " + authorityCount.str() + - ", additional record: " + additionalCount.str(); - - } - else // not likely - a DNS with no answers and no queries - { - return "DNS record without queries and answers, ID: " + tidAsString.str() + ";" + - " queries: " + queryCount.str() + - ", answers: " + answerCount.str() + - ", authorities: " + authorityCount.str() + - ", additional record: " + additionalCount.str(); - } -} - -IDnsResource* DnsLayer::getFirstResource(DnsResourceType resType) const -{ - switch (resType) - { - case DnsQueryType: - { - return m_FirstQuery; - } - case DnsAnswerType: - { - return m_FirstAnswer; - } - case DnsAuthorityType: - { - return m_FirstAuthority; - } - case DnsAdditionalType: - { - return m_FirstAdditional; - } - default: - return nullptr; - } -} - -void DnsLayer::setFirstResource(DnsResourceType resType, IDnsResource* resource) -{ - switch (resType) - { - case DnsQueryType: - { - m_FirstQuery = dynamic_cast(resource); - break; - } - case DnsAnswerType: - { - m_FirstAnswer = dynamic_cast(resource); - break; - } - case DnsAuthorityType: - { - m_FirstAuthority = dynamic_cast(resource); - break; - } - case DnsAdditionalType: - { - m_FirstAdditional = dynamic_cast(resource); - break; - } - default: - return; - } -} - -DnsResource* DnsLayer::addResource(DnsResourceType resType, const std::string& name, DnsType dnsType, DnsClass dnsClass, - uint32_t ttl, IDnsResourceData* data) -{ - // create new query on temporary buffer - uint8_t newResourceRawData[256]; - memset(newResourceRawData, 0, sizeof(newResourceRawData)); - - DnsResource* newResource = new DnsResource(newResourceRawData, resType); - - newResource->setDnsClass(dnsClass); - - newResource->setDnsType(dnsType); - - // cannot return false since layer shouldn't be extended or shortened in this stage - newResource->setName(name); - - newResource->setTTL(ttl); - - if (!newResource->setData(data)) - { - delete newResource; - PCPP_LOG_ERROR("Couldn't set new resource data"); - return nullptr; - } - - size_t newResourceOffsetInLayer = getBasicHeaderSize(); - IDnsResource* curResource = m_ResourceList; - while (curResource != nullptr && curResource->getType() <= resType) - { - newResourceOffsetInLayer += curResource->getSize(); - IDnsResource* nextResource = curResource->getNextResource(); - if (nextResource == nullptr || nextResource->getType() > resType) - break; - curResource = nextResource; - } - - - // set next resource for new resource. This must happen here for extendLayer to succeed - if (curResource != nullptr) - { - if (curResource->getType() > newResource->getType()) - newResource->setNexResource(m_ResourceList); - else - newResource->setNexResource(curResource->getNextResource()); - } - else //curResource != NULL - newResource->setNexResource(m_ResourceList); - - // extend layer to make room for the new resource - if (!extendLayer(newResourceOffsetInLayer, newResource->getSize(), newResource)) - { - PCPP_LOG_ERROR("Couldn't extend DNS layer, addResource failed"); - delete newResource; - return nullptr; - } - - // connect the new resource to layer - newResource->setDnsLayer(this, newResourceOffsetInLayer); - - // connect the new resource to the layer's resource list - if (curResource != nullptr) - { - curResource->setNexResource(newResource); - // this means the new resource is the first of it's type - if (curResource->getType() < newResource->getType()) - { - setFirstResource(resType, newResource); - } - // this means the new resource should be the first resource in the packet - else if (curResource->getType() > newResource->getType()) - { - m_ResourceList = newResource; - - setFirstResource(resType, newResource); - } - } - else // curResource != NULL, meaning this is the first resource in layer - { - m_ResourceList = newResource; - - setFirstResource(resType, newResource); - } - - return newResource; -} - - -DnsQuery* DnsLayer::addQuery(const std::string& name, DnsType dnsType, DnsClass dnsClass) -{ - // create new query on temporary buffer - uint8_t newQueryRawData[256]; - DnsQuery* newQuery = new DnsQuery(newQueryRawData); - - newQuery->setDnsClass(dnsClass); - newQuery->setDnsType(dnsType); - - // cannot return false since layer shouldn't be extended or shortened in this stage - newQuery->setName(name); - - - // find the offset in the layer to insert the new query - size_t newQueryOffsetInLayer = getBasicHeaderSize(); - DnsQuery* curQuery = getFirstQuery(); - while (curQuery != nullptr) - { - newQueryOffsetInLayer += curQuery->getSize(); - DnsQuery* nextQuery = getNextQuery(curQuery); - if (nextQuery == nullptr) - break; - curQuery = nextQuery; - - } - - // set next resource for new query. This must happen here for extendLayer to succeed - if (curQuery != nullptr) - newQuery->setNexResource(curQuery->getNextResource()); - else - newQuery->setNexResource(m_ResourceList); - - // extend layer to make room for the new query - if (!extendLayer(newQueryOffsetInLayer, newQuery->getSize(), newQuery)) - { - PCPP_LOG_ERROR("Couldn't extend DNS layer, addQuery failed"); - delete newQuery; - return nullptr; - } - - // connect the new query to layer - newQuery->setDnsLayer(this, newQueryOffsetInLayer); - - // connect the new query to the layer's resource list - if (curQuery != nullptr) - curQuery->setNexResource(newQuery); - else // curQuery == NULL, meaning this is the first query - { - m_ResourceList = newQuery; - m_FirstQuery = newQuery; - } - - // increase number of queries - getDnsHeader()->numberOfQuestions = htobe16(getQueryCount() + 1); - - return newQuery; -} - -DnsQuery* DnsLayer::addQuery(DnsQuery* const copyQuery) -{ - if (copyQuery == nullptr) - return nullptr; - - return addQuery(copyQuery->getName(), copyQuery->getDnsType(), copyQuery->getDnsClass()); -} - -bool DnsLayer::removeQuery(const std::string& queryNameToRemove, bool exactMatch) -{ - DnsQuery* queryToRemove = getQuery(queryNameToRemove, exactMatch); - if (queryToRemove == nullptr) - { - PCPP_LOG_DEBUG("Query not found"); - return false; - } - - return removeQuery(queryToRemove); -} - -bool DnsLayer::removeQuery(DnsQuery* queryToRemove) -{ - bool res = removeResource(queryToRemove); - if (res) - { - // decrease number of query records - getDnsHeader()->numberOfQuestions = htobe16(getQueryCount() - 1); - } - - return res; -} - -DnsResource* DnsLayer::addAnswer(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data) -{ - DnsResource* res = addResource(DnsAnswerType, name, dnsType, dnsClass, ttl, data); - if (res != nullptr) - { - // increase number of answer records - getDnsHeader()->numberOfAnswers = htobe16(getAnswerCount() + 1); - } - - return res; -} - -DnsResource* DnsLayer::addAnswer(DnsResource* const copyAnswer) -{ - if (copyAnswer == nullptr) - return nullptr; - - return addAnswer(copyAnswer->getName(), copyAnswer->getDnsType(), copyAnswer->getDnsClass(), copyAnswer->getTTL(), copyAnswer->getData().get()); -} - -bool DnsLayer::removeAnswer(const std::string& answerNameToRemove, bool exactMatch) -{ - DnsResource* answerToRemove = getAnswer(answerNameToRemove, exactMatch); - if (answerToRemove == nullptr) - { - PCPP_LOG_DEBUG("Answer record not found"); - return false; - } - - return removeAnswer(answerToRemove); -} - -bool DnsLayer::removeAnswer(DnsResource* answerToRemove) -{ - bool res = removeResource(answerToRemove); - if (res) - { - // decrease number of answer records - getDnsHeader()->numberOfAnswers = htobe16(getAnswerCount() - 1); - } - - return res; -} - - -DnsResource* DnsLayer::addAuthority(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data) -{ - DnsResource* res = addResource(DnsAuthorityType, name, dnsType, dnsClass, ttl, data); - if (res != nullptr) - { - // increase number of authority records - getDnsHeader()->numberOfAuthority = htobe16(getAuthorityCount() + 1); - } - - return res; -} - -DnsResource* DnsLayer::addAuthority(DnsResource* const copyAuthority) -{ - if (copyAuthority == nullptr) - return nullptr; - - return addAuthority(copyAuthority->getName(), copyAuthority->getDnsType(), copyAuthority->getDnsClass(), copyAuthority->getTTL(), copyAuthority->getData().get()); -} - -bool DnsLayer::removeAuthority(const std::string& authorityNameToRemove, bool exactMatch) -{ - DnsResource* authorityToRemove = getAuthority(authorityNameToRemove, exactMatch); - if (authorityToRemove == nullptr) - { - PCPP_LOG_DEBUG("Authority not found"); - return false; - } - - return removeAuthority(authorityToRemove); -} - -bool DnsLayer::removeAuthority(DnsResource* authorityToRemove) -{ - bool res = removeResource(authorityToRemove); - if (res) - { - // decrease number of authority records - getDnsHeader()->numberOfAuthority = htobe16(getAuthorityCount() - 1); - } - - return res; -} - - -DnsResource* DnsLayer::addAdditionalRecord(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data) -{ - DnsResource* res = addResource(DnsAdditionalType, name, dnsType, dnsClass, ttl, data); - if (res != nullptr) - { - // increase number of authority records - getDnsHeader()->numberOfAdditional = htobe16(getAdditionalRecordCount() + 1); - } - - return res; -} - -DnsResource* DnsLayer::addAdditionalRecord(const std::string& name, DnsType dnsType, uint16_t customData1, uint32_t customData2, IDnsResourceData* data) -{ - DnsResource* res = addAdditionalRecord(name, dnsType, DNS_CLASS_ANY, customData2, data); - if (res != nullptr) - { - res->setCustomDnsClass(customData1); - } - - return res; -} - -DnsResource* DnsLayer::addAdditionalRecord(DnsResource* const copyAdditionalRecord) -{ - if (copyAdditionalRecord == nullptr) - return nullptr; - - return addAdditionalRecord(copyAdditionalRecord->getName(), copyAdditionalRecord->getDnsType(), copyAdditionalRecord->getCustomDnsClass(), copyAdditionalRecord->getTTL(), copyAdditionalRecord->getData().get()); -} - -bool DnsLayer::removeAdditionalRecord(const std::string& additionalRecordNameToRemove, bool exactMatch) -{ - DnsResource* additionalRecordToRemove = getAdditionalRecord(additionalRecordNameToRemove, exactMatch); - if (additionalRecordToRemove == nullptr) - { - PCPP_LOG_DEBUG("Additional record not found"); - return false; - } - - return removeAdditionalRecord(additionalRecordToRemove); -} - -bool DnsLayer::removeAdditionalRecord(DnsResource* additionalRecordToRemove) -{ - bool res = removeResource(additionalRecordToRemove); - if (res) - { - // decrease number of additional records - getDnsHeader()->numberOfAdditional = htobe16(getAdditionalRecordCount() - 1); - } - - return res; -} - -bool DnsLayer::removeResource(IDnsResource* resourceToRemove) -{ - if (resourceToRemove == nullptr) - { - PCPP_LOG_DEBUG("resourceToRemove cannot be NULL"); - return false; - } - - // find the resource preceding resourceToRemove - IDnsResource* prevResource = m_ResourceList; - - if (m_ResourceList != resourceToRemove) - { - while (prevResource != nullptr) - { - IDnsResource* temp = prevResource->getNextResource(); - if (temp == resourceToRemove) - break; - - prevResource = temp; - } - } - - if (prevResource == nullptr) - { - PCPP_LOG_DEBUG("Resource not found"); - return false; - } - - // shorten the layer and fix offset in layer for all next DNS resources in the packet - if (!shortenLayer(resourceToRemove->m_OffsetInLayer, resourceToRemove->getSize(), resourceToRemove)) - { - PCPP_LOG_ERROR("Couldn't shorten the DNS layer, resource cannot be removed"); - return false; - } - - // remove resourceToRemove from the resources linked list - if (m_ResourceList != resourceToRemove) - { - prevResource->setNexResource(resourceToRemove->getNextResource()); - } - else - { - m_ResourceList = resourceToRemove->getNextResource(); - } - - // check whether resourceToRemove was the first of its type - if (getFirstResource(resourceToRemove->getType()) == resourceToRemove) - { - IDnsResource* nextResource = resourceToRemove->getNextResource(); - if (nextResource != nullptr && nextResource->getType() == resourceToRemove->getType()) - setFirstResource(resourceToRemove->getType(), nextResource); - else - setFirstResource(resourceToRemove->getType(), nullptr); - } - - // free resourceToRemove memory - delete resourceToRemove; - - return true; -} - - -// ~~~~~~~~~~~~~~~ -// DnsOverTcpLayer -// ~~~~~~~~~~~~~~~ - -uint16_t DnsOverTcpLayer::getTcpMessageLength() -{ - return be16toh(*(uint16_t*)m_Data); -} - -void DnsOverTcpLayer::setTcpMessageLength(uint16_t value) -{ - ((uint16_t*)m_Data)[0] = htobe16(value); -} - -void DnsOverTcpLayer::computeCalculateFields() -{ - setTcpMessageLength(m_DataLen - sizeof(uint16_t)); -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModuleDnsLayer + +#include "DnsLayer.h" +#include "Logger.h" +#include "IpAddress.h" +#include +#include +#include +#include +#include "EndianPortable.h" + +namespace pcpp +{ + +// ~~~~~~~~ +// DnsLayer +// ~~~~~~~~ + +DnsLayer::DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet) +{ + init(0, true); +} + +DnsLayer::DnsLayer() +{ + initNewLayer(0); +} + +DnsLayer::DnsLayer(const DnsLayer& other) : Layer(other) +{ + init(other.m_OffsetAdjustment, true); +} + +DnsLayer::DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, size_t offsetAdjustment) + : Layer(data, dataLen, prevLayer, packet) +{ + init(offsetAdjustment, true); +} + +DnsLayer::DnsLayer(size_t offsetAdjustment) +{ + initNewLayer(offsetAdjustment); +} + +DnsLayer& DnsLayer::operator=(const DnsLayer& other) +{ + Layer::operator=(other); + + IDnsResource* curResource = m_ResourceList; + while (curResource != nullptr) + { + IDnsResource* temp = curResource->getNextResource(); + delete curResource; + curResource = temp; + } + + init(other.m_OffsetAdjustment, true); + + return (*this); +} + +DnsLayer::~DnsLayer() +{ + IDnsResource* curResource = m_ResourceList; + while (curResource != nullptr) + { + IDnsResource* nextResource = curResource->getNextResource(); + delete curResource; + curResource = nextResource; + } +} + +void DnsLayer::init(size_t offsetAdjustment, bool callParseResource) +{ + m_OffsetAdjustment = offsetAdjustment; + m_Protocol = DNS; + m_ResourceList = nullptr; + + m_FirstQuery = nullptr; + m_FirstAnswer = nullptr; + m_FirstAuthority = nullptr; + m_FirstAdditional = nullptr; + + if (callParseResource) + parseResources(); +} + + +void DnsLayer::initNewLayer(size_t offsetAdjustment) +{ + m_OffsetAdjustment = offsetAdjustment; + const size_t headerLen = getBasicHeaderSize(); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + + init(m_OffsetAdjustment, false); +} + +size_t DnsLayer::getBasicHeaderSize() +{ + return sizeof(dnshdr) + m_OffsetAdjustment; +} + +dnshdr* DnsLayer::getDnsHeader() const +{ + uint8_t* ptr = m_Data + m_OffsetAdjustment; + return (dnshdr*)ptr; +} + +bool DnsLayer::extendLayer(int offsetInLayer, size_t numOfBytesToExtend, IDnsResource* resource) +{ + if (!Layer::extendLayer(offsetInLayer, numOfBytesToExtend)) + return false; + + IDnsResource* curResource = resource->getNextResource(); + while (curResource != nullptr) + { + curResource->m_OffsetInLayer += numOfBytesToExtend; + curResource = curResource->getNextResource(); + } + return true; +} + + +bool DnsLayer::shortenLayer(int offsetInLayer, size_t numOfBytesToShorten, IDnsResource* resource) +{ + if (!Layer::shortenLayer(offsetInLayer, numOfBytesToShorten)) + return false; + + IDnsResource* curResource = resource->getNextResource(); + while (curResource != nullptr) + { + curResource->m_OffsetInLayer -= numOfBytesToShorten; + curResource = curResource->getNextResource(); + } + return true; +} + + +void DnsLayer::parseResources() +{ + size_t offsetInPacket = getBasicHeaderSize(); + IDnsResource* curResource = m_ResourceList; + + uint16_t numOfQuestions = be16toh(getDnsHeader()->numberOfQuestions); + uint16_t numOfAnswers = be16toh(getDnsHeader()->numberOfAnswers); + uint16_t numOfAuthority = be16toh(getDnsHeader()->numberOfAuthority); + uint16_t numOfAdditional = be16toh(getDnsHeader()->numberOfAdditional); + + uint32_t numOfOtherResources = numOfQuestions + numOfAnswers + numOfAuthority + numOfAdditional; + + if (numOfOtherResources > 300) + { + PCPP_LOG_ERROR("DNS layer contains more than 300 resources, probably a bad packet. " + "Skipping parsing DNS resources"); + return; + } + + for (uint32_t i = 0; i < numOfOtherResources; i++) + { + DnsResourceType resType; + if (numOfQuestions > 0) + { + resType = DnsQueryType; + numOfQuestions--; + } + else if (numOfAnswers > 0) + { + resType = DnsAnswerType; + numOfAnswers--; + } + else if (numOfAuthority > 0) + { + resType = DnsAuthorityType; + numOfAuthority--; + } + else + { + resType = DnsAdditionalType; + numOfAdditional--; + } + + DnsResource* newResource = nullptr; + DnsQuery* newQuery = nullptr; + IDnsResource* newGenResource = nullptr; + if (resType == DnsQueryType) + { + newQuery = new DnsQuery(this, offsetInPacket); + newGenResource = newQuery; + offsetInPacket += newQuery->getSize(); + } + else + { + newResource = new DnsResource(this, offsetInPacket, resType); + newGenResource = newResource; + offsetInPacket += newResource->getSize(); + } + + if (offsetInPacket > m_DataLen) + { + //Parse packet failed, DNS resource is out of bounds. Probably a bad packet + delete newGenResource; + return; + } + + // this resource is the first resource + if (m_ResourceList == nullptr) + { + m_ResourceList = newGenResource; + curResource = m_ResourceList; + } + else + { + curResource->setNexResource(newGenResource); + curResource = curResource->getNextResource(); + } + + if (resType == DnsQueryType && m_FirstQuery == nullptr) + m_FirstQuery = newQuery; + else if (resType == DnsAnswerType && m_FirstAnswer == nullptr) + m_FirstAnswer = newResource; + else if (resType == DnsAuthorityType && m_FirstAuthority == nullptr) + m_FirstAuthority = newResource; + else if (resType == DnsAdditionalType && m_FirstAdditional == nullptr) + m_FirstAdditional = newResource; + } + +} + +IDnsResource* DnsLayer::getResourceByName(IDnsResource* startFrom, size_t resourceCount, const std::string& name, bool exactMatch) const +{ + size_t index = 0; + while (index < resourceCount) + { + if (startFrom == nullptr) + return nullptr; + + std::string resourceName = startFrom->getName(); + if (exactMatch && resourceName == name) + return startFrom; + else if (!exactMatch && resourceName.find(name) != std::string::npos) + return startFrom; + + startFrom = startFrom->getNextResource(); + + index++; + } + + return nullptr; +} + +DnsQuery* DnsLayer::getQuery(const std::string& name, bool exactMatch) const +{ + uint16_t numOfQueries = be16toh(getDnsHeader()->numberOfQuestions); + IDnsResource* res = getResourceByName(m_FirstQuery, numOfQueries, name, exactMatch); + if (res != nullptr) + return dynamic_cast(res); + return nullptr; +} + + +DnsQuery* DnsLayer::getFirstQuery() const +{ + return m_FirstQuery; +} + + +DnsQuery* DnsLayer::getNextQuery(DnsQuery* query) const +{ + if (query == nullptr + || query->getNextResource() == nullptr + || query->getType() != DnsQueryType + || query->getNextResource()->getType() != DnsQueryType) + return nullptr; + + return (DnsQuery*)(query->getNextResource()); +} + +size_t DnsLayer::getQueryCount() const +{ + return be16toh(getDnsHeader()->numberOfQuestions); +} + +DnsResource* DnsLayer::getAnswer(const std::string& name, bool exactMatch) const +{ + uint16_t numOfAnswers = be16toh(getDnsHeader()->numberOfAnswers); + IDnsResource* res = getResourceByName(m_FirstAnswer, numOfAnswers, name, exactMatch); + if (res != nullptr) + return dynamic_cast(res); + return nullptr; +} + +DnsResource* DnsLayer::getFirstAnswer() const +{ + return m_FirstAnswer; +} + +DnsResource* DnsLayer::getNextAnswer(DnsResource* answer) const +{ + if (answer == nullptr + || answer->getNextResource() == nullptr + || answer->getType() != DnsAnswerType + || answer->getNextResource()->getType() != DnsAnswerType) + return nullptr; + + return (DnsResource*)(answer->getNextResource()); +} + +size_t DnsLayer::getAnswerCount() const +{ + return be16toh(getDnsHeader()->numberOfAnswers); +} + +DnsResource* DnsLayer::getAuthority(const std::string& name, bool exactMatch) const +{ + uint16_t numOfAuthorities = be16toh(getDnsHeader()->numberOfAuthority); + IDnsResource* res = getResourceByName(m_FirstAuthority, numOfAuthorities, name, exactMatch); + if (res != nullptr) + return dynamic_cast(res); + return nullptr; +} + +DnsResource* DnsLayer::getFirstAuthority() const +{ + return m_FirstAuthority; +} + +DnsResource* DnsLayer::getNextAuthority(DnsResource* authority) const +{ + if (authority == nullptr + || authority->getNextResource() == nullptr + || authority->getType() != DnsAuthorityType + || authority->getNextResource()->getType() != DnsAuthorityType) + return nullptr; + + return (DnsResource*)(authority->getNextResource()); +} + +size_t DnsLayer::getAuthorityCount() const +{ + return be16toh(getDnsHeader()->numberOfAuthority); +} + +DnsResource* DnsLayer::getAdditionalRecord(const std::string& name, bool exactMatch) const +{ + uint16_t numOfAdditionalRecords = be16toh(getDnsHeader()->numberOfAdditional); + IDnsResource* res = getResourceByName(m_FirstAdditional, numOfAdditionalRecords, name, exactMatch); + if (res != nullptr) + return dynamic_cast(res); + return nullptr; +} + +DnsResource* DnsLayer::getFirstAdditionalRecord() const +{ + return m_FirstAdditional; +} + +DnsResource* DnsLayer::getNextAdditionalRecord(DnsResource* additionalRecord) const +{ + if (additionalRecord == nullptr + || additionalRecord->getNextResource() == nullptr + || additionalRecord->getType() != DnsAdditionalType + || additionalRecord->getNextResource()->getType() != DnsAdditionalType) + return nullptr; + + return (DnsResource*)(additionalRecord->getNextResource()); +} + +size_t DnsLayer::getAdditionalRecordCount() const +{ + return be16toh(getDnsHeader()->numberOfAdditional); +} + +std::string DnsLayer::toString() const +{ + std::ostringstream tidAsString; + tidAsString << be16toh(getDnsHeader()->transactionID); + + std::ostringstream queryCount; + queryCount << getQueryCount(); + + std::ostringstream answerCount; + answerCount << getAnswerCount(); + + std::ostringstream authorityCount; + authorityCount << getAuthorityCount(); + + std::ostringstream additionalCount; + additionalCount << getAdditionalRecordCount(); + + if (getDnsHeader()->queryOrResponse == 1) + { + return "DNS query response, ID: " + tidAsString.str() + ";" + + " queries: " + queryCount.str() + + ", answers: " + answerCount.str() + + ", authorities: " + authorityCount.str() + + ", additional record: " + additionalCount.str(); + } + else if (getDnsHeader()->queryOrResponse == 0) + { + return "DNS query, ID: " + tidAsString.str() + ";" + + " queries: " + queryCount.str() + + ", answers: " + answerCount.str() + + ", authorities: " + authorityCount.str() + + ", additional record: " + additionalCount.str(); + + } + else // not likely - a DNS with no answers and no queries + { + return "DNS record without queries and answers, ID: " + tidAsString.str() + ";" + + " queries: " + queryCount.str() + + ", answers: " + answerCount.str() + + ", authorities: " + authorityCount.str() + + ", additional record: " + additionalCount.str(); + } +} + +IDnsResource* DnsLayer::getFirstResource(DnsResourceType resType) const +{ + switch (resType) + { + case DnsQueryType: + { + return m_FirstQuery; + } + case DnsAnswerType: + { + return m_FirstAnswer; + } + case DnsAuthorityType: + { + return m_FirstAuthority; + } + case DnsAdditionalType: + { + return m_FirstAdditional; + } + default: + return nullptr; + } +} + +void DnsLayer::setFirstResource(DnsResourceType resType, IDnsResource* resource) +{ + switch (resType) + { + case DnsQueryType: + { + m_FirstQuery = dynamic_cast(resource); + break; + } + case DnsAnswerType: + { + m_FirstAnswer = dynamic_cast(resource); + break; + } + case DnsAuthorityType: + { + m_FirstAuthority = dynamic_cast(resource); + break; + } + case DnsAdditionalType: + { + m_FirstAdditional = dynamic_cast(resource); + break; + } + default: + return; + } +} + +DnsResource* DnsLayer::addResource(DnsResourceType resType, const std::string& name, DnsType dnsType, DnsClass dnsClass, + uint32_t ttl, IDnsResourceData* data) +{ + // create new query on temporary buffer + uint8_t newResourceRawData[256]; + memset(newResourceRawData, 0, sizeof(newResourceRawData)); + + DnsResource* newResource = new DnsResource(newResourceRawData, resType); + + newResource->setDnsClass(dnsClass); + + newResource->setDnsType(dnsType); + + // cannot return false since layer shouldn't be extended or shortened in this stage + newResource->setName(name); + + newResource->setTTL(ttl); + + if (!newResource->setData(data)) + { + delete newResource; + PCPP_LOG_ERROR("Couldn't set new resource data"); + return nullptr; + } + + size_t newResourceOffsetInLayer = getBasicHeaderSize(); + IDnsResource* curResource = m_ResourceList; + while (curResource != nullptr && curResource->getType() <= resType) + { + newResourceOffsetInLayer += curResource->getSize(); + IDnsResource* nextResource = curResource->getNextResource(); + if (nextResource == nullptr || nextResource->getType() > resType) + break; + curResource = nextResource; + } + + + // set next resource for new resource. This must happen here for extendLayer to succeed + if (curResource != nullptr) + { + if (curResource->getType() > newResource->getType()) + newResource->setNexResource(m_ResourceList); + else + newResource->setNexResource(curResource->getNextResource()); + } + else //curResource != NULL + newResource->setNexResource(m_ResourceList); + + // extend layer to make room for the new resource + if (!extendLayer(newResourceOffsetInLayer, newResource->getSize(), newResource)) + { + PCPP_LOG_ERROR("Couldn't extend DNS layer, addResource failed"); + delete newResource; + return nullptr; + } + + // connect the new resource to layer + newResource->setDnsLayer(this, newResourceOffsetInLayer); + + // connect the new resource to the layer's resource list + if (curResource != nullptr) + { + curResource->setNexResource(newResource); + // this means the new resource is the first of it's type + if (curResource->getType() < newResource->getType()) + { + setFirstResource(resType, newResource); + } + // this means the new resource should be the first resource in the packet + else if (curResource->getType() > newResource->getType()) + { + m_ResourceList = newResource; + + setFirstResource(resType, newResource); + } + } + else // curResource != NULL, meaning this is the first resource in layer + { + m_ResourceList = newResource; + + setFirstResource(resType, newResource); + } + + return newResource; +} + + +DnsQuery* DnsLayer::addQuery(const std::string& name, DnsType dnsType, DnsClass dnsClass) +{ + // create new query on temporary buffer + uint8_t newQueryRawData[256]; + DnsQuery* newQuery = new DnsQuery(newQueryRawData); + + newQuery->setDnsClass(dnsClass); + newQuery->setDnsType(dnsType); + + // cannot return false since layer shouldn't be extended or shortened in this stage + newQuery->setName(name); + + + // find the offset in the layer to insert the new query + size_t newQueryOffsetInLayer = getBasicHeaderSize(); + DnsQuery* curQuery = getFirstQuery(); + while (curQuery != nullptr) + { + newQueryOffsetInLayer += curQuery->getSize(); + DnsQuery* nextQuery = getNextQuery(curQuery); + if (nextQuery == nullptr) + break; + curQuery = nextQuery; + + } + + // set next resource for new query. This must happen here for extendLayer to succeed + if (curQuery != nullptr) + newQuery->setNexResource(curQuery->getNextResource()); + else + newQuery->setNexResource(m_ResourceList); + + // extend layer to make room for the new query + if (!extendLayer(newQueryOffsetInLayer, newQuery->getSize(), newQuery)) + { + PCPP_LOG_ERROR("Couldn't extend DNS layer, addQuery failed"); + delete newQuery; + return nullptr; + } + + // connect the new query to layer + newQuery->setDnsLayer(this, newQueryOffsetInLayer); + + // connect the new query to the layer's resource list + if (curQuery != nullptr) + curQuery->setNexResource(newQuery); + else // curQuery == NULL, meaning this is the first query + { + m_ResourceList = newQuery; + m_FirstQuery = newQuery; + } + + // increase number of queries + getDnsHeader()->numberOfQuestions = htobe16(getQueryCount() + 1); + + return newQuery; +} + +DnsQuery* DnsLayer::addQuery(DnsQuery* const copyQuery) +{ + if (copyQuery == nullptr) + return nullptr; + + return addQuery(copyQuery->getName(), copyQuery->getDnsType(), copyQuery->getDnsClass()); +} + +bool DnsLayer::removeQuery(const std::string& queryNameToRemove, bool exactMatch) +{ + DnsQuery* queryToRemove = getQuery(queryNameToRemove, exactMatch); + if (queryToRemove == nullptr) + { + PCPP_LOG_DEBUG("Query not found"); + return false; + } + + return removeQuery(queryToRemove); +} + +bool DnsLayer::removeQuery(DnsQuery* queryToRemove) +{ + bool res = removeResource(queryToRemove); + if (res) + { + // decrease number of query records + getDnsHeader()->numberOfQuestions = htobe16(getQueryCount() - 1); + } + + return res; +} + +DnsResource* DnsLayer::addAnswer(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data) +{ + DnsResource* res = addResource(DnsAnswerType, name, dnsType, dnsClass, ttl, data); + if (res != nullptr) + { + // increase number of answer records + getDnsHeader()->numberOfAnswers = htobe16(getAnswerCount() + 1); + } + + return res; +} + +DnsResource* DnsLayer::addAnswer(DnsResource* const copyAnswer) +{ + if (copyAnswer == nullptr) + return nullptr; + + return addAnswer(copyAnswer->getName(), copyAnswer->getDnsType(), copyAnswer->getDnsClass(), copyAnswer->getTTL(), copyAnswer->getData().get()); +} + +bool DnsLayer::removeAnswer(const std::string& answerNameToRemove, bool exactMatch) +{ + DnsResource* answerToRemove = getAnswer(answerNameToRemove, exactMatch); + if (answerToRemove == nullptr) + { + PCPP_LOG_DEBUG("Answer record not found"); + return false; + } + + return removeAnswer(answerToRemove); +} + +bool DnsLayer::removeAnswer(DnsResource* answerToRemove) +{ + bool res = removeResource(answerToRemove); + if (res) + { + // decrease number of answer records + getDnsHeader()->numberOfAnswers = htobe16(getAnswerCount() - 1); + } + + return res; +} + + +DnsResource* DnsLayer::addAuthority(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data) +{ + DnsResource* res = addResource(DnsAuthorityType, name, dnsType, dnsClass, ttl, data); + if (res != nullptr) + { + // increase number of authority records + getDnsHeader()->numberOfAuthority = htobe16(getAuthorityCount() + 1); + } + + return res; +} + +DnsResource* DnsLayer::addAuthority(DnsResource* const copyAuthority) +{ + if (copyAuthority == nullptr) + return nullptr; + + return addAuthority(copyAuthority->getName(), copyAuthority->getDnsType(), copyAuthority->getDnsClass(), copyAuthority->getTTL(), copyAuthority->getData().get()); +} + +bool DnsLayer::removeAuthority(const std::string& authorityNameToRemove, bool exactMatch) +{ + DnsResource* authorityToRemove = getAuthority(authorityNameToRemove, exactMatch); + if (authorityToRemove == nullptr) + { + PCPP_LOG_DEBUG("Authority not found"); + return false; + } + + return removeAuthority(authorityToRemove); +} + +bool DnsLayer::removeAuthority(DnsResource* authorityToRemove) +{ + bool res = removeResource(authorityToRemove); + if (res) + { + // decrease number of authority records + getDnsHeader()->numberOfAuthority = htobe16(getAuthorityCount() - 1); + } + + return res; +} + + +DnsResource* DnsLayer::addAdditionalRecord(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data) +{ + DnsResource* res = addResource(DnsAdditionalType, name, dnsType, dnsClass, ttl, data); + if (res != nullptr) + { + // increase number of authority records + getDnsHeader()->numberOfAdditional = htobe16(getAdditionalRecordCount() + 1); + } + + return res; +} + +DnsResource* DnsLayer::addAdditionalRecord(const std::string& name, DnsType dnsType, uint16_t customData1, uint32_t customData2, IDnsResourceData* data) +{ + DnsResource* res = addAdditionalRecord(name, dnsType, DNS_CLASS_ANY, customData2, data); + if (res != nullptr) + { + res->setCustomDnsClass(customData1); + } + + return res; +} + +DnsResource* DnsLayer::addAdditionalRecord(DnsResource* const copyAdditionalRecord) +{ + if (copyAdditionalRecord == nullptr) + return nullptr; + + return addAdditionalRecord(copyAdditionalRecord->getName(), copyAdditionalRecord->getDnsType(), copyAdditionalRecord->getCustomDnsClass(), copyAdditionalRecord->getTTL(), copyAdditionalRecord->getData().get()); +} + +bool DnsLayer::removeAdditionalRecord(const std::string& additionalRecordNameToRemove, bool exactMatch) +{ + DnsResource* additionalRecordToRemove = getAdditionalRecord(additionalRecordNameToRemove, exactMatch); + if (additionalRecordToRemove == nullptr) + { + PCPP_LOG_DEBUG("Additional record not found"); + return false; + } + + return removeAdditionalRecord(additionalRecordToRemove); +} + +bool DnsLayer::removeAdditionalRecord(DnsResource* additionalRecordToRemove) +{ + bool res = removeResource(additionalRecordToRemove); + if (res) + { + // decrease number of additional records + getDnsHeader()->numberOfAdditional = htobe16(getAdditionalRecordCount() - 1); + } + + return res; +} + +bool DnsLayer::removeResource(IDnsResource* resourceToRemove) +{ + if (resourceToRemove == nullptr) + { + PCPP_LOG_DEBUG("resourceToRemove cannot be NULL"); + return false; + } + + // find the resource preceding resourceToRemove + IDnsResource* prevResource = m_ResourceList; + + if (m_ResourceList != resourceToRemove) + { + while (prevResource != nullptr) + { + IDnsResource* temp = prevResource->getNextResource(); + if (temp == resourceToRemove) + break; + + prevResource = temp; + } + } + + if (prevResource == nullptr) + { + PCPP_LOG_DEBUG("Resource not found"); + return false; + } + + // shorten the layer and fix offset in layer for all next DNS resources in the packet + if (!shortenLayer(resourceToRemove->m_OffsetInLayer, resourceToRemove->getSize(), resourceToRemove)) + { + PCPP_LOG_ERROR("Couldn't shorten the DNS layer, resource cannot be removed"); + return false; + } + + // remove resourceToRemove from the resources linked list + if (m_ResourceList != resourceToRemove) + { + prevResource->setNexResource(resourceToRemove->getNextResource()); + } + else + { + m_ResourceList = resourceToRemove->getNextResource(); + } + + // check whether resourceToRemove was the first of its type + if (getFirstResource(resourceToRemove->getType()) == resourceToRemove) + { + IDnsResource* nextResource = resourceToRemove->getNextResource(); + if (nextResource != nullptr && nextResource->getType() == resourceToRemove->getType()) + setFirstResource(resourceToRemove->getType(), nextResource); + else + setFirstResource(resourceToRemove->getType(), nullptr); + } + + // free resourceToRemove memory + delete resourceToRemove; + + return true; +} + + +// ~~~~~~~~~~~~~~~ +// DnsOverTcpLayer +// ~~~~~~~~~~~~~~~ + +uint16_t DnsOverTcpLayer::getTcpMessageLength() +{ + return be16toh(*(uint16_t*)m_Data); +} + +void DnsOverTcpLayer::setTcpMessageLength(uint16_t value) +{ + ((uint16_t*)m_Data)[0] = htobe16(value); +} + +void DnsOverTcpLayer::computeCalculateFields() +{ + setTcpMessageLength(m_DataLen - sizeof(uint16_t)); +} + +} // namespace pcpp diff --git a/Packet++/src/GreLayer.cpp b/Packet++/src/GreLayer.cpp index d934ed9ad1..bae6aaefaa 100644 --- a/Packet++/src/GreLayer.cpp +++ b/Packet++/src/GreLayer.cpp @@ -1,618 +1,618 @@ -#define LOG_MODULE PacketLogModuleGreLayer - -#include "GreLayer.h" -#include "EthLayer.h" -#include "EthDot3Layer.h" -#include "IPv4Layer.h" -#include "IPv6Layer.h" -#include "PPPoELayer.h" -#include "VlanLayer.h" -#include "MplsLayer.h" -#include "PayloadLayer.h" -#include "PacketUtils.h" -#include "Logger.h" -#include "EndianPortable.h" - -// ============== -// GreLayer class -// ============== - -namespace pcpp -{ - -ProtocolType GreLayer::getGREVersion(uint8_t* greData, size_t greDataLen) -{ - if (greDataLen < sizeof(gre_basic_header)) - return UnknownProtocol; - - uint8_t version = *(greData+1); - version &= 0x07; - if (version == 0) - return GREv0; - else if (version == 1) - return GREv1; - else - return UnknownProtocol; -} - -uint8_t* GreLayer::getFieldValue(GreField field, bool returnOffsetEvenIfFieldMissing) const -{ - uint8_t* ptr = m_Data + sizeof(gre_basic_header); - - gre_basic_header* header = (gre_basic_header*)m_Data; - - for (int curFieldAsInt = static_cast(GreChecksumOrRouting); curFieldAsInt < 4 /* this value is out of scope of GreField enum values */; ++curFieldAsInt) - { - const GreField curField = static_cast(curFieldAsInt); - bool curFieldExists = false; - - uint8_t* origPtr = ptr; - - switch (curField) - { - case GreChecksumOrRouting: - if (header->checksumBit == 1 || header->routingBit == 1) - { - curFieldExists = true; - ptr += sizeof(uint32_t); - } - break; - case GreKey: - if (header->keyBit == 1) - { - curFieldExists = true; - ptr += sizeof(uint32_t); - } - break; - case GreSeq: - if (header->sequenceNumBit == 1) - { - curFieldExists = true; - ptr += sizeof(uint32_t); - } - break; - case GreAck: - if (header->ackSequenceNumBit == 1) - { - curFieldExists = true; - ptr += sizeof(uint32_t); - } - break; - default: // shouldn't get there - return nullptr; - } - - if (field == curField) - { - if (curFieldExists || returnOffsetEvenIfFieldMissing) - return origPtr; - - return nullptr; - } - } // for - - return nullptr; -} - -void GreLayer::computeCalculateFieldsInner() -{ - gre_basic_header* header = (gre_basic_header*)m_Data; - if (m_NextLayer != nullptr) - { - switch (m_NextLayer->getProtocol()) - { - case IPv4: - header->protocol = htobe16(PCPP_ETHERTYPE_IP); - break; - case IPv6: - header->protocol = htobe16(PCPP_ETHERTYPE_IPV6); - break; - case VLAN: - header->protocol = htobe16(PCPP_ETHERTYPE_VLAN); - break; - case MPLS: - header->protocol = htobe16(PCPP_ETHERTYPE_MPLS); - break; - case PPP_PPTP: - header->protocol = htobe16(PCPP_ETHERTYPE_PPP); - break; - case Ethernet: - header->protocol = htobe16(PCPP_ETHERTYPE_ETHBRIDGE); - break; - default: - break; - } - } -} - -bool GreLayer::getSequenceNumber(uint32_t& seqNumber) const -{ - gre_basic_header* header = (gre_basic_header*)m_Data; - - if (header->sequenceNumBit == 0) - return false; - - uint32_t* val = (uint32_t*)getFieldValue(GreSeq, false); - if (val == nullptr) - return false; - - seqNumber = be32toh(*val); - return true; -} - -bool GreLayer::setSequenceNumber(uint32_t seqNumber) -{ - gre_basic_header* header = (gre_basic_header*)m_Data; - - bool needToExtendLayer = false; - - if (header->sequenceNumBit == 0) - needToExtendLayer = true; - - uint8_t* offsetPtr = getFieldValue(GreSeq, true); - - int offset = offsetPtr - m_Data; - if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) - { - header->sequenceNumBit = 0; - PCPP_LOG_ERROR("Couldn't extend layer to set sequence number"); - return false; - } - - header = (gre_basic_header*)m_Data; - header->sequenceNumBit = 1; - uint32_t* seqPtr = (uint32_t*)(m_Data + offset); - *seqPtr = htobe32(seqNumber); - - return true; -} - -bool GreLayer::unsetSequenceNumber() -{ - gre_basic_header* header = (gre_basic_header*)m_Data; - - if (header->sequenceNumBit == 0) - { - PCPP_LOG_ERROR("Couldn't unset sequence number as it's already unset"); - return false; - } - - uint8_t* offsetPtr = getFieldValue(GreSeq, true); - - int offset = offsetPtr - m_Data; - if (!shortenLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Couldn't shorted layer to unset sequence number"); - return false; - } - - header = (gre_basic_header*)m_Data; - header->sequenceNumBit = 0; - return true; -} - -void GreLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; - - gre_basic_header* header = (gre_basic_header*)m_Data; - uint8_t* payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; - - switch (be16toh(header->protocol)) - { - case PCPP_ETHERTYPE_IP: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_ETHERTYPE_VLAN: - m_NextLayer = new VlanLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_MPLS: - m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_PPP: - m_NextLayer = new PPP_PPTPLayer(payload, payloadLen, this, m_Packet); - break; - case PCPP_ETHERTYPE_ETHBRIDGE: - if (EthLayer::isDataValid(payload, payloadLen)) - { - m_NextLayer = new EthLayer(payload, payloadLen, this, m_Packet); - } - else if (EthDot3Layer::isDataValid(payload, payloadLen)) - { - m_NextLayer = new EthDot3Layer(payload, payloadLen, this, m_Packet); - } - else - { - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } -} - -size_t GreLayer::getHeaderLen() const -{ - size_t result = sizeof(gre_basic_header); - - gre_basic_header* header = (gre_basic_header*)m_Data; - - if (header->checksumBit == 1 || header->routingBit == 1 ) - result += 4; - if (header->keyBit == 1) - result += 4; - if (header->sequenceNumBit == 1) - result += 4; - if (header->ackSequenceNumBit == 1) - result += 4; - - return result; -} - - - -// ================ -// GREv0Layer class -// ================ - - -GREv0Layer::GREv0Layer() -{ - const size_t headerLen = sizeof(gre_basic_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - m_Protocol = GREv0; -} - -bool GREv0Layer::getChecksum(uint16_t& checksum) -{ - if (getGreHeader()->checksumBit == 0) - return false; - - uint16_t* val = (uint16_t*)getFieldValue(GreChecksumOrRouting, false); - if (val == nullptr) - return false; - - checksum = be16toh(*val); - return true; -} - -bool GREv0Layer::setChecksum(uint16_t checksum) -{ - gre_basic_header* header = getGreHeader(); - - bool needToExtendLayer = false; - - if (header->routingBit == 0 && header->checksumBit == 0) - needToExtendLayer = true; - - uint8_t* offsetPtr = getFieldValue(GreChecksumOrRouting, true); - int offset = offsetPtr - m_Data; - // extend layer in 4 bytes to keep 4-byte alignment - if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Couldn't extend layer to set checksum"); - return false; - } - - uint16_t* checksumPtr = (uint16_t*)(m_Data + offset); - *checksumPtr = htobe16(checksum); - - // if layer was extended in 4 bytes, make sure the offset field stays 0 - if (needToExtendLayer) - { - checksumPtr++; - *checksumPtr = 0; - } - - header = getGreHeader(); - header->checksumBit = 1; - - return true; -} - -bool GREv0Layer::unsetChecksum() -{ - gre_basic_header* header = getGreHeader(); - - if (header->checksumBit == 0) - { - PCPP_LOG_ERROR("Couldn't unset checksum as it's already unset"); - return false; - } - - // if both routing and checksum are unset we need to shorted the layer - bool needToShortenLayer = (header->routingBit == 0); - - uint8_t* offsetPtr = getFieldValue(GreChecksumOrRouting, true); - int offset = offsetPtr - m_Data; - if (needToShortenLayer && !shortenLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Couldn't extend layer to unset checksum"); - return false; - } - - if (!needToShortenLayer) // meaning routing bit is set - only zero the checksum field - { - uint16_t* checksumPtr = (uint16_t*)(m_Data + offset); - *checksumPtr = 0; - } - - header = getGreHeader(); - header->checksumBit = 0; - - return true; -} - -bool GREv0Layer::getOffset(uint16_t& offset) const -{ - if (getGreHeader()->routingBit == 0) - return false; - - uint8_t* val = (uint8_t*)getFieldValue(GreChecksumOrRouting, false); - if (val == nullptr) - return false; - - offset = be16toh(*(val+2)); - return true; -} - -bool GREv0Layer::getKey(uint32_t& key) const -{ - if (getGreHeader()->keyBit == 0) - return false; - - uint32_t* val = (uint32_t*)getFieldValue(GreKey, false); - if (val == nullptr) - return false; - - key = be32toh(*val); - return true; -} - -bool GREv0Layer::setKey(uint32_t key) -{ - gre_basic_header* header = getGreHeader(); - - bool needToExtendLayer = false; - - if (header->keyBit == 0) - needToExtendLayer = true; - - uint8_t* offsetPtr = getFieldValue(GreKey, true); - - int offset = offsetPtr - m_Data; - if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) - { - header->keyBit = 0; - PCPP_LOG_ERROR("Couldn't extend layer to set key"); - return false; - } - - header = getGreHeader(); - header->keyBit = 1; - uint32_t* keyPtr = (uint32_t*)(m_Data + offset); - *keyPtr = htobe32(key); - - return true; -} - -bool GREv0Layer::unsetKey() -{ - gre_basic_header* header = getGreHeader(); - - if (header->keyBit == 0) - { - PCPP_LOG_ERROR("Couldn't unset key as it's already unset"); - return false; - } - - uint8_t* offsetPtr = getFieldValue(GreKey, true); - - int offset = offsetPtr - m_Data; - if (!shortenLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Couldn't shorted layer to unset key"); - return false; - } - - header = (gre_basic_header*)m_Data; - header->keyBit = 0; - return true; -} - -void GREv0Layer::computeCalculateFields() -{ - computeCalculateFieldsInner(); - - if (getGreHeader()->checksumBit == 0) - return; - - // calculate checksum - setChecksum(0); - - ScalarBuffer buffer; - buffer.buffer = (uint16_t*)m_Data; - buffer.len = m_DataLen; - size_t checksum = computeChecksum(&buffer, 1); - - setChecksum(checksum); -} - -std::string GREv0Layer::toString() const -{ - return "GRE Layer, version 0"; -} - - -// ================ -// GREv1Layer class -// ================ - -GREv1Layer::GREv1Layer(uint16_t callID) -{ - const size_t headerLen = sizeof(gre1_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - m_Protocol = GREv1; - - gre1_header* header = getGreHeader(); - header->keyBit = 1; - header->version = 1; - header->callID = htobe16(callID); -} - -bool GREv1Layer::getAcknowledgmentNum(uint32_t& ackNum) const -{ - if (getGreHeader()->ackSequenceNumBit == 0) - return false; - - uint32_t* val = (uint32_t*)getFieldValue(GreAck, false); - if (val == nullptr) - return false; - - ackNum = be32toh(*val); - return true; -} - -bool GREv1Layer::setAcknowledgmentNum(uint32_t ackNum) -{ - bool needToExtendLayer = false; - - gre1_header* header = getGreHeader(); - - if (header->ackSequenceNumBit == 0) - needToExtendLayer = true; - - uint8_t* offsetPtr = getFieldValue(GreAck, true); - int offset = offsetPtr - m_Data; - if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Couldn't extend layer to set ack number"); - return false; - } - - header = getGreHeader(); - header->ackSequenceNumBit = 1; - uint32_t* ackPtr = (uint32_t*)(m_Data + offset); - *ackPtr = htobe32(ackNum); - return true; -} - -bool GREv1Layer::unsetAcknowledgmentNum() -{ - gre1_header* header = getGreHeader(); - - if (header->ackSequenceNumBit == 0) - { - PCPP_LOG_ERROR("Couldn't unset ack number as it's already unset"); - return false; - } - - uint8_t* offsetPtr = getFieldValue(GreAck, true); - - int offset = offsetPtr - m_Data; - if (!shortenLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Couldn't shorted layer to unset ack number"); - return false; - } - - header = getGreHeader(); - header->ackSequenceNumBit = 0; - return true; -} - -void GREv1Layer::computeCalculateFields() -{ - computeCalculateFieldsInner(); - - getGreHeader()->payloadLength = htobe16(m_DataLen - getHeaderLen()); -} - -std::string GREv1Layer::toString() const -{ - return "GRE Layer, version 1"; -} - - - -// =================== -// PPP_PPTPLayer class -// =================== - -PPP_PPTPLayer::PPP_PPTPLayer(uint8_t address, uint8_t control) -{ - const size_t headerLen = sizeof(ppp_pptp_header); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - m_Protocol = PPP_PPTP; - - ppp_pptp_header* header = getPPP_PPTPHeader(); - header->address = address; - header->control = control; -} - - -void PPP_PPTPLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; - - uint8_t* payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; - - switch (be16toh(getPPP_PPTPHeader()->protocol)) - { - case PCPP_PPP_IP: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_PPP_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - } -} - -void PPP_PPTPLayer::computeCalculateFields() -{ - ppp_pptp_header* header = getPPP_PPTPHeader(); - if (m_NextLayer != nullptr) - { - switch (m_NextLayer->getProtocol()) - { - case IPv4: - header->protocol = htobe16(PCPP_PPP_IP); - break; - case IPv6: - header->protocol = htobe16(PCPP_PPP_IPV6); - break; - default: - break; - } - } - else - header->protocol = 0; -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModuleGreLayer + +#include "GreLayer.h" +#include "EthLayer.h" +#include "EthDot3Layer.h" +#include "IPv4Layer.h" +#include "IPv6Layer.h" +#include "PPPoELayer.h" +#include "VlanLayer.h" +#include "MplsLayer.h" +#include "PayloadLayer.h" +#include "PacketUtils.h" +#include "Logger.h" +#include "EndianPortable.h" + +// ============== +// GreLayer class +// ============== + +namespace pcpp +{ + +ProtocolType GreLayer::getGREVersion(uint8_t* greData, size_t greDataLen) +{ + if (greDataLen < sizeof(gre_basic_header)) + return UnknownProtocol; + + uint8_t version = *(greData+1); + version &= 0x07; + if (version == 0) + return GREv0; + else if (version == 1) + return GREv1; + else + return UnknownProtocol; +} + +uint8_t* GreLayer::getFieldValue(GreField field, bool returnOffsetEvenIfFieldMissing) const +{ + uint8_t* ptr = m_Data + sizeof(gre_basic_header); + + gre_basic_header* header = (gre_basic_header*)m_Data; + + for (int curFieldAsInt = static_cast(GreChecksumOrRouting); curFieldAsInt < 4 /* this value is out of scope of GreField enum values */; ++curFieldAsInt) + { + const GreField curField = static_cast(curFieldAsInt); + bool curFieldExists = false; + + uint8_t* origPtr = ptr; + + switch (curField) + { + case GreChecksumOrRouting: + if (header->checksumBit == 1 || header->routingBit == 1) + { + curFieldExists = true; + ptr += sizeof(uint32_t); + } + break; + case GreKey: + if (header->keyBit == 1) + { + curFieldExists = true; + ptr += sizeof(uint32_t); + } + break; + case GreSeq: + if (header->sequenceNumBit == 1) + { + curFieldExists = true; + ptr += sizeof(uint32_t); + } + break; + case GreAck: + if (header->ackSequenceNumBit == 1) + { + curFieldExists = true; + ptr += sizeof(uint32_t); + } + break; + default: // shouldn't get there + return nullptr; + } + + if (field == curField) + { + if (curFieldExists || returnOffsetEvenIfFieldMissing) + return origPtr; + + return nullptr; + } + } // for + + return nullptr; +} + +void GreLayer::computeCalculateFieldsInner() +{ + gre_basic_header* header = (gre_basic_header*)m_Data; + if (m_NextLayer != nullptr) + { + switch (m_NextLayer->getProtocol()) + { + case IPv4: + header->protocol = htobe16(PCPP_ETHERTYPE_IP); + break; + case IPv6: + header->protocol = htobe16(PCPP_ETHERTYPE_IPV6); + break; + case VLAN: + header->protocol = htobe16(PCPP_ETHERTYPE_VLAN); + break; + case MPLS: + header->protocol = htobe16(PCPP_ETHERTYPE_MPLS); + break; + case PPP_PPTP: + header->protocol = htobe16(PCPP_ETHERTYPE_PPP); + break; + case Ethernet: + header->protocol = htobe16(PCPP_ETHERTYPE_ETHBRIDGE); + break; + default: + break; + } + } +} + +bool GreLayer::getSequenceNumber(uint32_t& seqNumber) const +{ + gre_basic_header* header = (gre_basic_header*)m_Data; + + if (header->sequenceNumBit == 0) + return false; + + uint32_t* val = (uint32_t*)getFieldValue(GreSeq, false); + if (val == nullptr) + return false; + + seqNumber = be32toh(*val); + return true; +} + +bool GreLayer::setSequenceNumber(uint32_t seqNumber) +{ + gre_basic_header* header = (gre_basic_header*)m_Data; + + bool needToExtendLayer = false; + + if (header->sequenceNumBit == 0) + needToExtendLayer = true; + + uint8_t* offsetPtr = getFieldValue(GreSeq, true); + + int offset = offsetPtr - m_Data; + if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) + { + header->sequenceNumBit = 0; + PCPP_LOG_ERROR("Couldn't extend layer to set sequence number"); + return false; + } + + header = (gre_basic_header*)m_Data; + header->sequenceNumBit = 1; + uint32_t* seqPtr = (uint32_t*)(m_Data + offset); + *seqPtr = htobe32(seqNumber); + + return true; +} + +bool GreLayer::unsetSequenceNumber() +{ + gre_basic_header* header = (gre_basic_header*)m_Data; + + if (header->sequenceNumBit == 0) + { + PCPP_LOG_ERROR("Couldn't unset sequence number as it's already unset"); + return false; + } + + uint8_t* offsetPtr = getFieldValue(GreSeq, true); + + int offset = offsetPtr - m_Data; + if (!shortenLayer(offset, sizeof(uint32_t))) + { + PCPP_LOG_ERROR("Couldn't shorted layer to unset sequence number"); + return false; + } + + header = (gre_basic_header*)m_Data; + header->sequenceNumBit = 0; + return true; +} + +void GreLayer::parseNextLayer() +{ + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; + + gre_basic_header* header = (gre_basic_header*)m_Data; + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; + + switch (be16toh(header->protocol)) + { + case PCPP_ETHERTYPE_IP: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_IPV6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PCPP_ETHERTYPE_VLAN: + m_NextLayer = new VlanLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_MPLS: + m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_PPP: + m_NextLayer = new PPP_PPTPLayer(payload, payloadLen, this, m_Packet); + break; + case PCPP_ETHERTYPE_ETHBRIDGE: + if (EthLayer::isDataValid(payload, payloadLen)) + { + m_NextLayer = new EthLayer(payload, payloadLen, this, m_Packet); + } + else if (EthDot3Layer::isDataValid(payload, payloadLen)) + { + m_NextLayer = new EthDot3Layer(payload, payloadLen, this, m_Packet); + } + else + { + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } +} + +size_t GreLayer::getHeaderLen() const +{ + size_t result = sizeof(gre_basic_header); + + gre_basic_header* header = (gre_basic_header*)m_Data; + + if (header->checksumBit == 1 || header->routingBit == 1 ) + result += 4; + if (header->keyBit == 1) + result += 4; + if (header->sequenceNumBit == 1) + result += 4; + if (header->ackSequenceNumBit == 1) + result += 4; + + return result; +} + + + +// ================ +// GREv0Layer class +// ================ + + +GREv0Layer::GREv0Layer() +{ + const size_t headerLen = sizeof(gre_basic_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + m_Protocol = GREv0; +} + +bool GREv0Layer::getChecksum(uint16_t& checksum) +{ + if (getGreHeader()->checksumBit == 0) + return false; + + uint16_t* val = (uint16_t*)getFieldValue(GreChecksumOrRouting, false); + if (val == nullptr) + return false; + + checksum = be16toh(*val); + return true; +} + +bool GREv0Layer::setChecksum(uint16_t checksum) +{ + gre_basic_header* header = getGreHeader(); + + bool needToExtendLayer = false; + + if (header->routingBit == 0 && header->checksumBit == 0) + needToExtendLayer = true; + + uint8_t* offsetPtr = getFieldValue(GreChecksumOrRouting, true); + int offset = offsetPtr - m_Data; + // extend layer in 4 bytes to keep 4-byte alignment + if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) + { + PCPP_LOG_ERROR("Couldn't extend layer to set checksum"); + return false; + } + + uint16_t* checksumPtr = (uint16_t*)(m_Data + offset); + *checksumPtr = htobe16(checksum); + + // if layer was extended in 4 bytes, make sure the offset field stays 0 + if (needToExtendLayer) + { + checksumPtr++; + *checksumPtr = 0; + } + + header = getGreHeader(); + header->checksumBit = 1; + + return true; +} + +bool GREv0Layer::unsetChecksum() +{ + gre_basic_header* header = getGreHeader(); + + if (header->checksumBit == 0) + { + PCPP_LOG_ERROR("Couldn't unset checksum as it's already unset"); + return false; + } + + // if both routing and checksum are unset we need to shorted the layer + bool needToShortenLayer = (header->routingBit == 0); + + uint8_t* offsetPtr = getFieldValue(GreChecksumOrRouting, true); + int offset = offsetPtr - m_Data; + if (needToShortenLayer && !shortenLayer(offset, sizeof(uint32_t))) + { + PCPP_LOG_ERROR("Couldn't extend layer to unset checksum"); + return false; + } + + if (!needToShortenLayer) // meaning routing bit is set - only zero the checksum field + { + uint16_t* checksumPtr = (uint16_t*)(m_Data + offset); + *checksumPtr = 0; + } + + header = getGreHeader(); + header->checksumBit = 0; + + return true; +} + +bool GREv0Layer::getOffset(uint16_t& offset) const +{ + if (getGreHeader()->routingBit == 0) + return false; + + uint8_t* val = (uint8_t*)getFieldValue(GreChecksumOrRouting, false); + if (val == nullptr) + return false; + + offset = be16toh(*(val+2)); + return true; +} + +bool GREv0Layer::getKey(uint32_t& key) const +{ + if (getGreHeader()->keyBit == 0) + return false; + + uint32_t* val = (uint32_t*)getFieldValue(GreKey, false); + if (val == nullptr) + return false; + + key = be32toh(*val); + return true; +} + +bool GREv0Layer::setKey(uint32_t key) +{ + gre_basic_header* header = getGreHeader(); + + bool needToExtendLayer = false; + + if (header->keyBit == 0) + needToExtendLayer = true; + + uint8_t* offsetPtr = getFieldValue(GreKey, true); + + int offset = offsetPtr - m_Data; + if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) + { + header->keyBit = 0; + PCPP_LOG_ERROR("Couldn't extend layer to set key"); + return false; + } + + header = getGreHeader(); + header->keyBit = 1; + uint32_t* keyPtr = (uint32_t*)(m_Data + offset); + *keyPtr = htobe32(key); + + return true; +} + +bool GREv0Layer::unsetKey() +{ + gre_basic_header* header = getGreHeader(); + + if (header->keyBit == 0) + { + PCPP_LOG_ERROR("Couldn't unset key as it's already unset"); + return false; + } + + uint8_t* offsetPtr = getFieldValue(GreKey, true); + + int offset = offsetPtr - m_Data; + if (!shortenLayer(offset, sizeof(uint32_t))) + { + PCPP_LOG_ERROR("Couldn't shorted layer to unset key"); + return false; + } + + header = (gre_basic_header*)m_Data; + header->keyBit = 0; + return true; +} + +void GREv0Layer::computeCalculateFields() +{ + computeCalculateFieldsInner(); + + if (getGreHeader()->checksumBit == 0) + return; + + // calculate checksum + setChecksum(0); + + ScalarBuffer buffer; + buffer.buffer = (uint16_t*)m_Data; + buffer.len = m_DataLen; + size_t checksum = computeChecksum(&buffer, 1); + + setChecksum(checksum); +} + +std::string GREv0Layer::toString() const +{ + return "GRE Layer, version 0"; +} + + +// ================ +// GREv1Layer class +// ================ + +GREv1Layer::GREv1Layer(uint16_t callID) +{ + const size_t headerLen = sizeof(gre1_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + m_Protocol = GREv1; + + gre1_header* header = getGreHeader(); + header->keyBit = 1; + header->version = 1; + header->callID = htobe16(callID); +} + +bool GREv1Layer::getAcknowledgmentNum(uint32_t& ackNum) const +{ + if (getGreHeader()->ackSequenceNumBit == 0) + return false; + + uint32_t* val = (uint32_t*)getFieldValue(GreAck, false); + if (val == nullptr) + return false; + + ackNum = be32toh(*val); + return true; +} + +bool GREv1Layer::setAcknowledgmentNum(uint32_t ackNum) +{ + bool needToExtendLayer = false; + + gre1_header* header = getGreHeader(); + + if (header->ackSequenceNumBit == 0) + needToExtendLayer = true; + + uint8_t* offsetPtr = getFieldValue(GreAck, true); + int offset = offsetPtr - m_Data; + if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t))) + { + PCPP_LOG_ERROR("Couldn't extend layer to set ack number"); + return false; + } + + header = getGreHeader(); + header->ackSequenceNumBit = 1; + uint32_t* ackPtr = (uint32_t*)(m_Data + offset); + *ackPtr = htobe32(ackNum); + return true; +} + +bool GREv1Layer::unsetAcknowledgmentNum() +{ + gre1_header* header = getGreHeader(); + + if (header->ackSequenceNumBit == 0) + { + PCPP_LOG_ERROR("Couldn't unset ack number as it's already unset"); + return false; + } + + uint8_t* offsetPtr = getFieldValue(GreAck, true); + + int offset = offsetPtr - m_Data; + if (!shortenLayer(offset, sizeof(uint32_t))) + { + PCPP_LOG_ERROR("Couldn't shorted layer to unset ack number"); + return false; + } + + header = getGreHeader(); + header->ackSequenceNumBit = 0; + return true; +} + +void GREv1Layer::computeCalculateFields() +{ + computeCalculateFieldsInner(); + + getGreHeader()->payloadLength = htobe16(m_DataLen - getHeaderLen()); +} + +std::string GREv1Layer::toString() const +{ + return "GRE Layer, version 1"; +} + + + +// =================== +// PPP_PPTPLayer class +// =================== + +PPP_PPTPLayer::PPP_PPTPLayer(uint8_t address, uint8_t control) +{ + const size_t headerLen = sizeof(ppp_pptp_header); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + m_Protocol = PPP_PPTP; + + ppp_pptp_header* header = getPPP_PPTPHeader(); + header->address = address; + header->control = control; +} + + +void PPP_PPTPLayer::parseNextLayer() +{ + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; + + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; + + switch (be16toh(getPPP_PPTPHeader()->protocol)) + { + case PCPP_PPP_IP: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PCPP_PPP_IPV6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + } +} + +void PPP_PPTPLayer::computeCalculateFields() +{ + ppp_pptp_header* header = getPPP_PPTPHeader(); + if (m_NextLayer != nullptr) + { + switch (m_NextLayer->getProtocol()) + { + case IPv4: + header->protocol = htobe16(PCPP_PPP_IP); + break; + case IPv6: + header->protocol = htobe16(PCPP_PPP_IPV6); + break; + default: + break; + } + } + else + header->protocol = 0; +} + +} // namespace pcpp diff --git a/Packet++/src/HttpLayer.cpp b/Packet++/src/HttpLayer.cpp index 006f31792c..620e177e17 100644 --- a/Packet++/src/HttpLayer.cpp +++ b/Packet++/src/HttpLayer.cpp @@ -1,1347 +1,1347 @@ -#define LOG_MODULE PacketLogModuleHttpLayer - -#include "Logger.h" -#include "GeneralUtils.h" -#include "HttpLayer.h" -#include -#include -#include -#include -#include - -namespace pcpp -{ - - -// -------- Class HttpMessage ----------------- - - -HeaderField* HttpMessage::addField(const std::string& fieldName, const std::string& fieldValue) -{ - if (getFieldByName(fieldName) != nullptr) - { - PCPP_LOG_ERROR("Field '" << fieldName << "' already exists!"); - return nullptr; - } - - return TextBasedProtocolMessage::addField(fieldName, fieldValue); -} - -HeaderField* HttpMessage::addField(const HeaderField& newField) -{ - if (getFieldByName(newField.getFieldName()) != nullptr) - { - PCPP_LOG_ERROR("Field '" << newField.getFieldName() << "' already exists!"); - return nullptr; - } - - return TextBasedProtocolMessage::addField(newField); -} - -HeaderField* HttpMessage::insertField(HeaderField* prevField, const std::string& fieldName, const std::string& fieldValue) -{ - if (getFieldByName(fieldName) != nullptr) - { - PCPP_LOG_ERROR("Field '" << fieldName << "' already exists!"); - return nullptr; - } - - return TextBasedProtocolMessage::insertField(prevField, fieldName, fieldValue); -} - -HeaderField* HttpMessage::insertField(HeaderField* prevField, const HeaderField& newField) -{ - if (getFieldByName(newField.getFieldName()) != nullptr) - { - PCPP_LOG_ERROR("Field '" << newField.getFieldName() << "' already exists!"); - return nullptr; - } - - return TextBasedProtocolMessage::insertField(prevField, newField); -} - - - -// -------- Class HttpRequestLayer ----------------- - -HttpRequestLayer::HttpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : HttpMessage(data, dataLen, prevLayer, packet) -{ - m_Protocol = HTTPRequest; - m_FirstLine = new HttpRequestFirstLine(this); - m_FieldsOffset = m_FirstLine->getSize(); - parseFields(); -} - -HttpRequestLayer::HttpRequestLayer(HttpMethod method, const std::string& uri, HttpVersion version) -{ - m_Protocol = HTTPRequest; - m_FirstLine = new HttpRequestFirstLine(this, method, version, uri); - m_FieldsOffset = m_FirstLine->getSize(); -} - -HttpRequestLayer::HttpRequestLayer(const HttpRequestLayer& other) : HttpMessage(other) -{ - m_FirstLine = new HttpRequestFirstLine(this); -} - -HttpRequestLayer& HttpRequestLayer::operator=(const HttpRequestLayer& other) -{ - HttpMessage::operator=(other); - - if (m_FirstLine != nullptr) - delete m_FirstLine; - - m_FirstLine = new HttpRequestFirstLine(this); - - return *this; -} - - -std::string HttpRequestLayer::getUrl() const -{ - HeaderField* hostField = getFieldByName(PCPP_HTTP_HOST_FIELD); - if (hostField == nullptr) - return m_FirstLine->getUri(); - - return hostField->getFieldValue() + m_FirstLine->getUri(); -} - -HttpRequestLayer::~HttpRequestLayer() -{ - delete m_FirstLine; -} - -std::string HttpRequestLayer::toString() const -{ - static const int maxLengthToPrint = 120; - std::string result = "HTTP request, "; - int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line - if (size <= 0) - { - result += std::string("CORRUPT DATA"); - return result; - } - if (size <= maxLengthToPrint) - { - char* firstLine = new char[size+1]; - strncpy(firstLine, (char*)m_Data, size); - firstLine[size] = 0; - result += std::string(firstLine); - delete[] firstLine; - } - else - { - char firstLine[maxLengthToPrint+1]; - strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3); - firstLine[maxLengthToPrint-3] = '.'; - firstLine[maxLengthToPrint-2] = '.'; - firstLine[maxLengthToPrint-1] = '.'; - firstLine[maxLengthToPrint] = 0; - result += std::string(firstLine); - } - - return result; -} - - - - - - - -// -------- Class HttpRequestFirstLine ----------------- - - -const std::string MethodEnumToString[9] = { - "GET", - "HEAD", - "POST", - "PUT", - "DELETE", - "TRACE", - "OPTIONS", - "CONNECT", - "PATCH" -}; - -const std::string VersionEnumToString[3] = { - "0.9", - "1.0", - "1.1" -}; - -HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest) : m_HttpRequest(httpRequest) -{ - m_Method = parseMethod((char*)m_HttpRequest->m_Data, m_HttpRequest->getDataLen()); - if (m_Method == HttpRequestLayer::HttpMethodUnknown) - { - m_UriOffset = -1; - PCPP_LOG_DEBUG("Couldn't resolve HTTP request method"); - m_IsComplete = false; - m_Version = HttpVersionUnknown; - m_VersionOffset = -1; - m_FirstLineEndOffset = m_HttpRequest->getDataLen(); - return; - } - else - m_UriOffset = MethodEnumToString[m_Method].length() + 1; - - parseVersion(); - if(m_VersionOffset < 0) - { - m_IsComplete = false; - m_FirstLineEndOffset = m_HttpRequest->getDataLen(); - return; - } - - char* endOfFirstLine; - if ((endOfFirstLine = (char*)memchr((char*)(m_HttpRequest->m_Data + m_VersionOffset), '\n', m_HttpRequest->m_DataLen-(size_t)m_VersionOffset)) != nullptr) - { - m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpRequest->m_Data + 1; - m_IsComplete = true; - } - else - { - m_FirstLineEndOffset = m_HttpRequest->getDataLen(); - m_IsComplete = false; - } - - if (Logger::getInstance().isDebugEnabled(PacketLogModuleHttpLayer)) - { - std::string method = m_Method == HttpRequestLayer::HttpMethodUnknown? "Unknown" : MethodEnumToString[m_Method]; - PCPP_LOG_DEBUG( - "Method='" << method << "'; " - << "HTTP version='" << VersionEnumToString[m_Version] << "'; " - << "URI='" << getUri() << "'"); - } -} - -HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest, HttpRequestLayer::HttpMethod method, HttpVersion version, const std::string &uri) -{ - try // throw(HttpRequestFirstLineException) - { - if (method == HttpRequestLayer::HttpMethodUnknown) - { - m_Exception.setMessage("Method supplied was HttpMethodUnknown"); - throw m_Exception; - } - - if (version == HttpVersionUnknown) - { - m_Exception.setMessage("Version supplied was HttpVersionUnknown"); - throw m_Exception; - } - - m_HttpRequest = httpRequest; - - m_Method = method; - m_Version = version; - - std::string firstLine = MethodEnumToString[m_Method] + " " + uri + " " + "HTTP/" + VersionEnumToString[m_Version] + "\r\n"; - - m_UriOffset = MethodEnumToString[m_Method].length() + 1; - m_FirstLineEndOffset = firstLine.length(); - m_VersionOffset = m_UriOffset + uri.length() + 6; - - m_HttpRequest->m_DataLen = firstLine.length(); - m_HttpRequest->m_Data = new uint8_t[m_HttpRequest->m_DataLen]; - memcpy(m_HttpRequest->m_Data, firstLine.c_str(), m_HttpRequest->m_DataLen); - - m_IsComplete = true; - } - catch(const HttpRequestFirstLineException&) - { - throw; - } - catch(...) - { - std::terminate(); - } -} - -HttpRequestLayer::HttpMethod HttpRequestFirstLine::parseMethod(char* data, size_t dataLen) -{ - if (dataLen < 4) - { - return HttpRequestLayer::HttpMethodUnknown; - } - - switch (data[0]) - { - case 'G': - if (data[1] == 'E' && data[2] == 'T' && data[3] == ' ') - return HttpRequestLayer::HttpGET; - else - return HttpRequestLayer::HttpMethodUnknown; - break; - - case 'D': - if (dataLen < 7) - return HttpRequestLayer::HttpMethodUnknown; - else if (data[1] == 'E' && data[2] == 'L' && data[3] == 'E' && data[4] == 'T' && data[5] == 'E' && data[6] == ' ') - return HttpRequestLayer::HttpDELETE; - else - return HttpRequestLayer::HttpMethodUnknown; - break; - - case 'C': - if (dataLen < 8) - return HttpRequestLayer::HttpMethodUnknown; - else if (data[1] == 'O' && data[2] == 'N' && data[3] == 'N' && data[4] == 'E' && data[5] == 'C' && data[6] == 'T' && data[7] == ' ') - return HttpRequestLayer::HttpCONNECT; - else - return HttpRequestLayer::HttpMethodUnknown; - break; - - case 'T': - if (dataLen < 6) - return HttpRequestLayer::HttpMethodUnknown; - else if (data[1] == 'R' && data[2] == 'A' && data[3] == 'C' && data[4] == 'E' && data[5] == ' ') - return HttpRequestLayer::HttpTRACE; - else - return HttpRequestLayer::HttpMethodUnknown; - break; - - - case 'H': - if (dataLen < 5) - return HttpRequestLayer::HttpMethodUnknown; - else if (data[1] == 'E' && data[2] == 'A' && data[3] == 'D' && data[4] == ' ') - return HttpRequestLayer::HttpHEAD; - else - return HttpRequestLayer::HttpMethodUnknown; - break; - - case 'O': - if (dataLen < 8) - return HttpRequestLayer::HttpMethodUnknown; - else if (data[1] == 'P' && data[2] == 'T' && data[3] == 'I' && data[4] == 'O' && data[5] == 'N' && data[6] == 'S' && data[7] == ' ') - return HttpRequestLayer::HttpOPTIONS; - else - return HttpRequestLayer::HttpMethodUnknown; - break; - - case 'P': - switch (data[1]) - { - case 'U': - if (data[2] == 'T' && data[3] == ' ') - return HttpRequestLayer::HttpPUT; - else - return HttpRequestLayer::HttpMethodUnknown; - break; - - case 'O': - if (dataLen < 5) - return HttpRequestLayer::HttpMethodUnknown; - else if (data[2] == 'S' && data[3] == 'T' && data[4] == ' ') - return HttpRequestLayer::HttpPOST; - else - return HttpRequestLayer::HttpMethodUnknown; - break; - - case 'A': - if (dataLen < 6) - return HttpRequestLayer::HttpMethodUnknown; - else if (data[2] == 'T' && data[3] == 'C' && data[4] == 'H' && data[5] == ' ') - return HttpRequestLayer::HttpPATCH; - else - return HttpRequestLayer::HttpMethodUnknown; - break; - - default: - return HttpRequestLayer::HttpMethodUnknown; - } - break; - - default: - return HttpRequestLayer::HttpMethodUnknown; - } -} - -void HttpRequestFirstLine::parseVersion() -{ - char* data = (char*)(m_HttpRequest->m_Data + m_UriOffset); - char* verPos = cross_platform_memmem(data, m_HttpRequest->getDataLen() - m_UriOffset, " HTTP/", 6); - if (verPos == nullptr) - { - m_Version = HttpVersionUnknown; - m_VersionOffset = -1; - return; - } - - // verify packet doesn't end before the version, meaning still left place for " HTTP/x.y" (9 chars) - if ((verPos + 9 - (char*)m_HttpRequest->m_Data) > m_HttpRequest->getDataLen()) - { - m_Version = HttpVersionUnknown; - m_VersionOffset = -1; - return; - } - - //skip " HTTP/" (6 chars) - verPos += 6; - switch (verPos[0]) - { - case '0': - if (verPos[1] == '.' && verPos[2] == '9') - m_Version = ZeroDotNine; - else - m_Version = HttpVersionUnknown; - break; - - case '1': - if (verPos[1] == '.' && verPos[2] == '0') - m_Version = OneDotZero; - else if (verPos[1] == '.' && verPos[2] == '1') - m_Version = OneDotOne; - else - m_Version = HttpVersionUnknown; - break; - - default: - m_Version = HttpVersionUnknown; - } - - m_VersionOffset = verPos - (char*)m_HttpRequest->m_Data; -} - -bool HttpRequestFirstLine::setMethod(HttpRequestLayer::HttpMethod newMethod) -{ - if (newMethod == HttpRequestLayer::HttpMethodUnknown) - { - PCPP_LOG_ERROR("Requested method is HttpMethodUnknown"); - return false; - } - - //extend or shorten layer - int lengthDifference = MethodEnumToString[newMethod].length() - MethodEnumToString[m_Method].length(); - if (lengthDifference > 0) - { - if (!m_HttpRequest->extendLayer(0, lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - else if (lengthDifference < 0) - { - if (!m_HttpRequest->shortenLayer(0, 0-lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - - } - } - - if (lengthDifference != 0) - m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), lengthDifference); - - memcpy(m_HttpRequest->m_Data, MethodEnumToString[newMethod].c_str(), MethodEnumToString[newMethod].length()); - - m_Method = newMethod; - m_UriOffset += lengthDifference; - m_VersionOffset += lengthDifference; - - return true; -} - -std::string HttpRequestFirstLine::getUri() const -{ - std::string result; - if (m_UriOffset != -1 && m_VersionOffset != -1) - result.assign((const char*)m_HttpRequest->m_Data + m_UriOffset, m_VersionOffset - 6 - m_UriOffset); - - //else first line is illegal, return empty string - - return result; -} - -bool HttpRequestFirstLine::setUri(std::string newUri) -{ - // make sure the new URI begins with "/" - if (newUri.compare(0, 1, "/") != 0) - newUri = "/" + newUri; - - //extend or shorten layer - std::string currentUri = getUri(); - int lengthDifference = newUri.length() - currentUri.length(); - if (lengthDifference > 0) - { - if (!m_HttpRequest->extendLayer(m_UriOffset, lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - else if (lengthDifference < 0) - { - if (!m_HttpRequest->shortenLayer(m_UriOffset, 0-lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - - if (lengthDifference != 0) - m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), lengthDifference); - - memcpy(m_HttpRequest->m_Data + m_UriOffset, newUri.c_str(), newUri.length()); - - m_VersionOffset += lengthDifference; - - return true; -} - -void HttpRequestFirstLine::setVersion(HttpVersion newVersion) -{ - if (m_VersionOffset == -1) - return; - - if (newVersion == HttpVersionUnknown) - return; - - char* verPos = (char*)(m_HttpRequest->m_Data + m_VersionOffset); - memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3); - - m_Version = newVersion; -} - - - - - - -// -------- Class HttpResponseLayer ----------------- - - - -const std::string StatusCodeEnumToString[80] = { - "Continue", - "Switching Protocols", - "Processing", - "OK", - "Created", - "Accepted", - "Non-Authoritative Information", - "No Content", - "Reset Content", - "Partial Content", - "Multi-Status", - "Already Reported", - "IM Used", - "Multiple Choices", - "Moved Permanently", - "Found", - "See Other", - "Not Modified", - "Use Proxy", - "Switch Proxy", - "Temporary Redirect", - "Permanent Redirect", - "Bad Request", - "Unauthorized", - "Payment Required", - "Forbidden", - "Not Found", - "Method Not Allowed", - "Not Acceptable", - "Proxy Authentication Required", - "Request Timeout", - "Conflict", - "Gone", - "Length Required", - "Precondition Failed", - "Request Entity Too Large", - "Request-URI Too Long", - "Unsupported Media Type", - "Requested Range Not Satisfiable", - "Expectation Failed", - "I'm a teapot", - "Authentication Timeout", - "Method Failure", - "Unprocessable Entity", - "Locked", - "Failed Dependency", - "Upgrade Required", - "Precondition Required", - "Too Many Requests", - "Request Header Fields Too Large", - "Login Timeout", - "No Response", - "Retry With", - "Blocked by Windows Parental Controls", - "Unavailable For Legal Reasons", - "Request Header Too Large", - "Cert Error", - "No Cert", - "HTTP to HTTPS", - "Token expired/invalid", - "Client Closed Request", - "Internal Server Error", - "Not Implemented", - "Bad Gateway", - "Service Unavailable", - "Gateway Timeout", - "HTTP Version Not Supported", - "Variant Also Negotiates", - "Insufficient Storage", - "Loop Detected", - "Bandwidth Limit Exceeded", - "Not Extended", - "Network Authentication Required", - "Origin Error", - "Web server is down", - "Connection timed out", - "Proxy Declined Request", - "A timeout occurred", - "Network read timeout error", - "Network connect timeout error" -}; - - -const int StatusCodeEnumToInt[80] = { - 100, - 101, - 102, - 200, - 201, - 202, - 203, - 204, - 205, - 206, - 207, - 208, - 226, - 300, - 301, - 302, - 303, - 304, - 305, - 306, - 307, - 308, - 400, - 401, - 402, - 403, - 404, - 405, - 406, - 407, - 408, - 409, - 410, - 411, - 412, - 413, - 414, - 415, - 416, - 417, - 418, - 419, - 420, - 422, - 423, - 424, - 426, - 428, - 429, - 431, - 440, - 444, - 449, - 450, - 451, - 494, - 495, - 496, - 497, - 498, - 499, - 500, - 501, - 502, - 503, - 504, - 505, - 506, - 507, - 508, - 509, - 510, - 511, - 520, - 521, - 522, - 523, - 524, - 598, - 599 -}; - - - -HttpResponseLayer::HttpResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : HttpMessage(data, dataLen, prevLayer, packet) -{ - m_Protocol = HTTPResponse; - m_FirstLine = new HttpResponseFirstLine(this); - m_FieldsOffset = m_FirstLine->getSize(); - parseFields(); -} - -HttpResponseLayer::HttpResponseLayer(HttpVersion version, HttpResponseLayer::HttpResponseStatusCode statusCode, std::string statusCodeString) -{ - m_Protocol = HTTPResponse; - m_FirstLine = new HttpResponseFirstLine(this, version, statusCode, std::move(statusCodeString)); - m_FieldsOffset = m_FirstLine->getSize(); -} - -HttpResponseLayer::~HttpResponseLayer() -{ - delete m_FirstLine; -} - - -HttpResponseLayer::HttpResponseLayer(const HttpResponseLayer& other) : HttpMessage(other) -{ - m_FirstLine = new HttpResponseFirstLine(this); -} - -HttpResponseLayer& HttpResponseLayer::operator=(const HttpResponseLayer& other) -{ - HttpMessage::operator=(other); - - if (m_FirstLine != nullptr) - delete m_FirstLine; - - m_FirstLine = new HttpResponseFirstLine(this); - - return *this; -} - - -HeaderField* HttpResponseLayer::setContentLength(int contentLength, const std::string &prevFieldName) -{ - std::ostringstream contentLengthAsString; - contentLengthAsString << contentLength; - std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD); - HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); - if (contentLengthField == nullptr) - { - HeaderField* prevField = getFieldByName(prevFieldName); - contentLengthField = insertField(prevField, PCPP_HTTP_CONTENT_LENGTH_FIELD, contentLengthAsString.str()); - } - else - contentLengthField->setFieldValue(contentLengthAsString.str()); - - return contentLengthField; -} - -int HttpResponseLayer::getContentLength() const -{ - std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD); - std::transform(contentLengthFieldName.begin(), contentLengthFieldName.end(), contentLengthFieldName.begin(), ::tolower); - HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); - if (contentLengthField != nullptr) - return atoi(contentLengthField->getFieldValue().c_str()); - return 0; -} - -std::string HttpResponseLayer::toString() const -{ - static const int maxLengthToPrint = 120; - std::string result = "HTTP response, "; - int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line - if (size <= maxLengthToPrint) - { - char* firstLine = new char[size+1]; - strncpy(firstLine, (char*)m_Data, size); - firstLine[size] = 0; - result += std::string(firstLine); - delete[] firstLine; - } - else - { - char firstLine[maxLengthToPrint+1]; - strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3); - firstLine[maxLengthToPrint-3] = '.'; - firstLine[maxLengthToPrint-2] = '.'; - firstLine[maxLengthToPrint-1] = '.'; - firstLine[maxLengthToPrint] = 0; - result += std::string(firstLine); - } - - return result; -} - - - - - - - - -// -------- Class HttpResponseFirstLine ----------------- - - - -int HttpResponseFirstLine::getStatusCodeAsInt() const -{ - return StatusCodeEnumToInt[m_StatusCode]; -} - -std::string HttpResponseFirstLine::getStatusCodeString() const -{ - std::string result; - const int statusStringOffset = 13; - if (m_StatusCode != HttpResponseLayer::HttpStatusCodeUnknown) - { - int statusStringEndOffset = m_FirstLineEndOffset - 2; - if ((*(m_HttpResponse->m_Data + statusStringEndOffset)) != '\r') - statusStringEndOffset++; - result.assign((char*)(m_HttpResponse->m_Data + statusStringOffset), statusStringEndOffset-statusStringOffset); - } - - //else first line is illegal, return empty string - - return result; -} - -bool HttpResponseFirstLine::setStatusCode(HttpResponseLayer::HttpResponseStatusCode newStatusCode, std::string statusCodeString) -{ - if (newStatusCode == HttpResponseLayer::HttpStatusCodeUnknown) - { - PCPP_LOG_ERROR("Requested status code is HttpStatusCodeUnknown"); - return false; - } - - //extend or shorten layer - - size_t statusStringOffset = 13; - if (statusCodeString == "") - statusCodeString = StatusCodeEnumToString[newStatusCode]; - int lengthDifference = statusCodeString.length() - getStatusCodeString().length(); - if (lengthDifference > 0) - { - if (!m_HttpResponse->extendLayer(statusStringOffset, lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - else if (lengthDifference < 0) - { - if (!m_HttpResponse->shortenLayer(statusStringOffset, 0-lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - - } - } - - if (lengthDifference != 0) - m_HttpResponse->shiftFieldsOffset(m_HttpResponse->getFirstField(), lengthDifference); - - // copy status string - memcpy(m_HttpResponse->m_Data+statusStringOffset, statusCodeString.c_str(), statusCodeString.length()); - - // change status code - std::ostringstream statusCodeAsString; - statusCodeAsString << StatusCodeEnumToInt[newStatusCode]; - memcpy(m_HttpResponse->m_Data+9, statusCodeAsString.str().c_str(), 3); - - m_StatusCode = newStatusCode; - - m_FirstLineEndOffset += lengthDifference; - - return true; - -} - -void HttpResponseFirstLine::setVersion(HttpVersion newVersion) -{ - if (newVersion == HttpVersionUnknown) - return; - - char* verPos = (char*)(m_HttpResponse->m_Data + 5); - memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3); - - m_Version = newVersion; -} - -HttpResponseLayer::HttpResponseStatusCode HttpResponseFirstLine::validateStatusCode(char* data, size_t dataLen, HttpResponseLayer::HttpResponseStatusCode potentialCode) -{ - if (dataLen < 1 || data[0] != ' ') - return HttpResponseLayer::HttpStatusCodeUnknown; - - return potentialCode; -} - -HttpResponseLayer::HttpResponseStatusCode HttpResponseFirstLine::parseStatusCode(char* data, size_t dataLen) -{ - if (parseVersion(data, dataLen) == HttpVersionUnknown) - return HttpResponseLayer::HttpStatusCodeUnknown; - - // minimum data should be 12B long: "HTTP/x.y XXX" - if (dataLen < 12) - return HttpResponseLayer::HttpStatusCodeUnknown; - - char* statusCodeData = data + 9; - size_t statusCodeDataLen = dataLen - 9; - - switch (statusCodeData[0]) - { - case '1': - switch (statusCodeData[1]) - { - case '0': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http100Continue); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http101SwitchingProtocols); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http102Processing); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - }; - - break; - - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - }; - - break; - case '2': - switch (statusCodeData[1]) - { - case '0': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http200OK); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http201Created); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http202Accepted); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http203NonAuthoritativeInformation); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http204NoContent); - case '5': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http205ResetContent); - case '6': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http206PartialContent); - case '7': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http207MultiStatus); - case '8': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http208AlreadyReported); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - - }; - - break; - case '2': - switch (statusCodeData[2]) - { - case '6': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http226IMUsed); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - }; - - break; - - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - - }; - - break; - - case '3': - switch (statusCodeData[1]) - { - case '0': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http300MultipleChoices); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http301MovedPermanently); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http302); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http303SeeOther); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http304NotModified); - case '5': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http305UseProxy); - case '6': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http306SwitchProxy); - case '7': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http307TemporaryRedirect); - case '8': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http308PermanentRedirect); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - - }; - - break; - - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - }; - - break; - - case '4': - switch (statusCodeData[1]) - { - case '0': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http400BadRequest); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http401Unauthorized); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http402PaymentRequired); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http403Forbidden); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http404NotFound); - case '5': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http405MethodNotAllowed); - case '6': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http406NotAcceptable); - case '7': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http407ProxyAuthenticationRequired); - case '8': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http408RequestTimeout); - case '9': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http409Conflict); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - - }; - - break; - - case '1': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http410Gone); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http411LengthRequired); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http412PreconditionFailed); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http413RequestEntityTooLarge); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http414RequestURITooLong); - case '5': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http415UnsupportedMediaType); - case '6': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http416RequestedRangeNotSatisfiable); - case '7': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http417ExpectationFailed); - case '8': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http418Imateapot); - case '9': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http419AuthenticationTimeout); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - - }; - - break; - - case '2': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http420); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http422UnprocessableEntity); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http423Locked); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http424FailedDependency); - case '6': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http426UpgradeRequired); - case '8': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http428PreconditionRequired); - case '9': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http429TooManyRequests); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - - }; - - break; - - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http431RequestHeaderFieldsTooLarge); - - case '4': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http440LoginTimeout); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http444NoResponse); - case '9': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http449RetryWith); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - }; - - break; - - case '5': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http450BlockedByWindowsParentalControls); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http451); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - }; - - break; - - case '9': - switch (statusCodeData[2]) - { - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http494RequestHeaderTooLarge); - case '5': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http495CertError); - case '6': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http496NoCert); - case '7': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http497HTTPtoHTTPS); - case '8': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http498TokenExpiredInvalid); - case '9': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http499); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - }; - - break; - - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - }; - - break; - - case '5': - switch (statusCodeData[1]) - { - case '0': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http500InternalServerError); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http501NotImplemented); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http502BadGateway); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http503ServiceUnavailable); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http504GatewayTimeout); - case '5': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http505HTTPVersionNotSupported); - case '6': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http506VariantAlsoNegotiates); - case '7': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http507InsufficientStorage); - case '8': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http508LoopDetected); - case '9': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http509BandwidthLimitExceeded); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - - }; - - break; - - case '1': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http510NotExtended); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http511NetworkAuthenticationRequired); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - }; - - break; - - case '2': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http520OriginError); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http521WebServerIsDown); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http522ConnectionTimedOut); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http523ProxyDeclinedRequest); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http524aTimeoutOccurred); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - }; - - break; - - case '9': - switch (statusCodeData[2]) - { - case '8': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http598NetworkReadTimeoutError); - case '9': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http599NetworkConnectTimeoutError); - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - }; - - break; - - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - }; - - break; - - default: - return HttpResponseLayer::HttpStatusCodeUnknown; - } - - return HttpResponseLayer::HttpStatusCodeUnknown; -} - -HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse) : m_HttpResponse(httpResponse) -{ - m_Version = parseVersion((char*)m_HttpResponse->m_Data, m_HttpResponse->getDataLen()); - if (m_Version == HttpVersionUnknown) - { - m_StatusCode = HttpResponseLayer::HttpStatusCodeUnknown; - } - else - { - m_StatusCode = parseStatusCode((char*)m_HttpResponse->m_Data, m_HttpResponse->getDataLen()); - } - - - char* endOfFirstLine; - if ((endOfFirstLine = (char*)memchr((char*)(m_HttpResponse->m_Data), '\n', m_HttpResponse->m_DataLen)) != nullptr) - { - m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpResponse->m_Data + 1; - m_IsComplete = true; - } - else - { - m_FirstLineEndOffset = m_HttpResponse->getDataLen(); - m_IsComplete = false; - } - - if (Logger::getInstance().isDebugEnabled(PacketLogModuleHttpLayer)) - { - std::string version = (m_Version == HttpVersionUnknown ? "Unknown" : VersionEnumToString[m_Version]); - int statusCode = (m_StatusCode == HttpResponseLayer::HttpStatusCodeUnknown ? 0 : StatusCodeEnumToInt[m_StatusCode]); - PCPP_LOG_DEBUG("Version='" << version << "'; Status code=" << statusCode << " '" << getStatusCodeString() << "'"); - } -} - - -HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse, HttpVersion version, HttpResponseLayer::HttpResponseStatusCode statusCode, std::string statusCodeString) -{ - if (statusCode == HttpResponseLayer::HttpStatusCodeUnknown) - { - m_Exception.setMessage("Status code supplied was HttpStatusCodeUnknown"); - throw m_Exception; - } - - if (version == HttpVersionUnknown) - { - m_Exception.setMessage("Version supplied was HttpVersionUnknown"); - throw m_Exception; - } - - m_HttpResponse = httpResponse; - - m_StatusCode = statusCode; - m_Version = version; - - std::ostringstream statusCodeAsString; - statusCodeAsString << StatusCodeEnumToInt[m_StatusCode]; - if (statusCodeString == "") - statusCodeString = StatusCodeEnumToString[m_StatusCode]; - std::string firstLine = "HTTP/" + VersionEnumToString[m_Version] + " " + statusCodeAsString.str() + " " + statusCodeString + "\r\n"; - - m_FirstLineEndOffset = firstLine.length(); - - m_HttpResponse->m_DataLen = firstLine.length(); - m_HttpResponse->m_Data = new uint8_t[m_HttpResponse->m_DataLen]; - memcpy(m_HttpResponse->m_Data, firstLine.c_str(), m_HttpResponse->m_DataLen); - - m_IsComplete = true; -} - -HttpVersion HttpResponseFirstLine::parseVersion(char* data, size_t dataLen) -{ - if (dataLen < 8) // "HTTP/x.y" - { - PCPP_LOG_DEBUG("HTTP response length < 8, cannot identify version"); - return HttpVersionUnknown; - } - - if (data[0] != 'H' || data[1] != 'T' || data[2] != 'T' || data[3] != 'P' || data[4] != '/') - { - PCPP_LOG_DEBUG("HTTP response does not begin with 'HTTP/'"); - return HttpVersionUnknown; - } - - char* verPos = data + 5; - switch (verPos[0]) - { - case '0': - if (verPos[1] == '.' && verPos[2] == '9') - return ZeroDotNine; - else - return HttpVersionUnknown; - break; - - case '1': - if (verPos[1] == '.' && verPos[2] == '0') - return OneDotZero; - else if (verPos[1] == '.' && verPos[2] == '1') - return OneDotOne; - else - return HttpVersionUnknown; - break; - - default: - return HttpVersionUnknown; - } -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModuleHttpLayer + +#include "Logger.h" +#include "GeneralUtils.h" +#include "HttpLayer.h" +#include +#include +#include +#include +#include + +namespace pcpp +{ + + +// -------- Class HttpMessage ----------------- + + +HeaderField* HttpMessage::addField(const std::string& fieldName, const std::string& fieldValue) +{ + if (getFieldByName(fieldName) != nullptr) + { + PCPP_LOG_ERROR("Field '" << fieldName << "' already exists!"); + return nullptr; + } + + return TextBasedProtocolMessage::addField(fieldName, fieldValue); +} + +HeaderField* HttpMessage::addField(const HeaderField& newField) +{ + if (getFieldByName(newField.getFieldName()) != nullptr) + { + PCPP_LOG_ERROR("Field '" << newField.getFieldName() << "' already exists!"); + return nullptr; + } + + return TextBasedProtocolMessage::addField(newField); +} + +HeaderField* HttpMessage::insertField(HeaderField* prevField, const std::string& fieldName, const std::string& fieldValue) +{ + if (getFieldByName(fieldName) != nullptr) + { + PCPP_LOG_ERROR("Field '" << fieldName << "' already exists!"); + return nullptr; + } + + return TextBasedProtocolMessage::insertField(prevField, fieldName, fieldValue); +} + +HeaderField* HttpMessage::insertField(HeaderField* prevField, const HeaderField& newField) +{ + if (getFieldByName(newField.getFieldName()) != nullptr) + { + PCPP_LOG_ERROR("Field '" << newField.getFieldName() << "' already exists!"); + return nullptr; + } + + return TextBasedProtocolMessage::insertField(prevField, newField); +} + + + +// -------- Class HttpRequestLayer ----------------- + +HttpRequestLayer::HttpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : HttpMessage(data, dataLen, prevLayer, packet) +{ + m_Protocol = HTTPRequest; + m_FirstLine = new HttpRequestFirstLine(this); + m_FieldsOffset = m_FirstLine->getSize(); + parseFields(); +} + +HttpRequestLayer::HttpRequestLayer(HttpMethod method, const std::string& uri, HttpVersion version) +{ + m_Protocol = HTTPRequest; + m_FirstLine = new HttpRequestFirstLine(this, method, version, uri); + m_FieldsOffset = m_FirstLine->getSize(); +} + +HttpRequestLayer::HttpRequestLayer(const HttpRequestLayer& other) : HttpMessage(other) +{ + m_FirstLine = new HttpRequestFirstLine(this); +} + +HttpRequestLayer& HttpRequestLayer::operator=(const HttpRequestLayer& other) +{ + HttpMessage::operator=(other); + + if (m_FirstLine != nullptr) + delete m_FirstLine; + + m_FirstLine = new HttpRequestFirstLine(this); + + return *this; +} + + +std::string HttpRequestLayer::getUrl() const +{ + HeaderField* hostField = getFieldByName(PCPP_HTTP_HOST_FIELD); + if (hostField == nullptr) + return m_FirstLine->getUri(); + + return hostField->getFieldValue() + m_FirstLine->getUri(); +} + +HttpRequestLayer::~HttpRequestLayer() +{ + delete m_FirstLine; +} + +std::string HttpRequestLayer::toString() const +{ + static const int maxLengthToPrint = 120; + std::string result = "HTTP request, "; + int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line + if (size <= 0) + { + result += std::string("CORRUPT DATA"); + return result; + } + if (size <= maxLengthToPrint) + { + char* firstLine = new char[size+1]; + strncpy(firstLine, (char*)m_Data, size); + firstLine[size] = 0; + result += std::string(firstLine); + delete[] firstLine; + } + else + { + char firstLine[maxLengthToPrint+1]; + strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3); + firstLine[maxLengthToPrint-3] = '.'; + firstLine[maxLengthToPrint-2] = '.'; + firstLine[maxLengthToPrint-1] = '.'; + firstLine[maxLengthToPrint] = 0; + result += std::string(firstLine); + } + + return result; +} + + + + + + + +// -------- Class HttpRequestFirstLine ----------------- + + +const std::string MethodEnumToString[9] = { + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "TRACE", + "OPTIONS", + "CONNECT", + "PATCH" +}; + +const std::string VersionEnumToString[3] = { + "0.9", + "1.0", + "1.1" +}; + +HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest) : m_HttpRequest(httpRequest) +{ + m_Method = parseMethod((char*)m_HttpRequest->m_Data, m_HttpRequest->getDataLen()); + if (m_Method == HttpRequestLayer::HttpMethodUnknown) + { + m_UriOffset = -1; + PCPP_LOG_DEBUG("Couldn't resolve HTTP request method"); + m_IsComplete = false; + m_Version = HttpVersionUnknown; + m_VersionOffset = -1; + m_FirstLineEndOffset = m_HttpRequest->getDataLen(); + return; + } + else + m_UriOffset = MethodEnumToString[m_Method].length() + 1; + + parseVersion(); + if(m_VersionOffset < 0) + { + m_IsComplete = false; + m_FirstLineEndOffset = m_HttpRequest->getDataLen(); + return; + } + + char* endOfFirstLine; + if ((endOfFirstLine = (char*)memchr((char*)(m_HttpRequest->m_Data + m_VersionOffset), '\n', m_HttpRequest->m_DataLen-(size_t)m_VersionOffset)) != nullptr) + { + m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpRequest->m_Data + 1; + m_IsComplete = true; + } + else + { + m_FirstLineEndOffset = m_HttpRequest->getDataLen(); + m_IsComplete = false; + } + + if (Logger::getInstance().isDebugEnabled(PacketLogModuleHttpLayer)) + { + std::string method = m_Method == HttpRequestLayer::HttpMethodUnknown? "Unknown" : MethodEnumToString[m_Method]; + PCPP_LOG_DEBUG( + "Method='" << method << "'; " + << "HTTP version='" << VersionEnumToString[m_Version] << "'; " + << "URI='" << getUri() << "'"); + } +} + +HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest, HttpRequestLayer::HttpMethod method, HttpVersion version, const std::string &uri) +{ + try // throw(HttpRequestFirstLineException) + { + if (method == HttpRequestLayer::HttpMethodUnknown) + { + m_Exception.setMessage("Method supplied was HttpMethodUnknown"); + throw m_Exception; + } + + if (version == HttpVersionUnknown) + { + m_Exception.setMessage("Version supplied was HttpVersionUnknown"); + throw m_Exception; + } + + m_HttpRequest = httpRequest; + + m_Method = method; + m_Version = version; + + std::string firstLine = MethodEnumToString[m_Method] + " " + uri + " " + "HTTP/" + VersionEnumToString[m_Version] + "\r\n"; + + m_UriOffset = MethodEnumToString[m_Method].length() + 1; + m_FirstLineEndOffset = firstLine.length(); + m_VersionOffset = m_UriOffset + uri.length() + 6; + + m_HttpRequest->m_DataLen = firstLine.length(); + m_HttpRequest->m_Data = new uint8_t[m_HttpRequest->m_DataLen]; + memcpy(m_HttpRequest->m_Data, firstLine.c_str(), m_HttpRequest->m_DataLen); + + m_IsComplete = true; + } + catch(const HttpRequestFirstLineException&) + { + throw; + } + catch(...) + { + std::terminate(); + } +} + +HttpRequestLayer::HttpMethod HttpRequestFirstLine::parseMethod(char* data, size_t dataLen) +{ + if (dataLen < 4) + { + return HttpRequestLayer::HttpMethodUnknown; + } + + switch (data[0]) + { + case 'G': + if (data[1] == 'E' && data[2] == 'T' && data[3] == ' ') + return HttpRequestLayer::HttpGET; + else + return HttpRequestLayer::HttpMethodUnknown; + break; + + case 'D': + if (dataLen < 7) + return HttpRequestLayer::HttpMethodUnknown; + else if (data[1] == 'E' && data[2] == 'L' && data[3] == 'E' && data[4] == 'T' && data[5] == 'E' && data[6] == ' ') + return HttpRequestLayer::HttpDELETE; + else + return HttpRequestLayer::HttpMethodUnknown; + break; + + case 'C': + if (dataLen < 8) + return HttpRequestLayer::HttpMethodUnknown; + else if (data[1] == 'O' && data[2] == 'N' && data[3] == 'N' && data[4] == 'E' && data[5] == 'C' && data[6] == 'T' && data[7] == ' ') + return HttpRequestLayer::HttpCONNECT; + else + return HttpRequestLayer::HttpMethodUnknown; + break; + + case 'T': + if (dataLen < 6) + return HttpRequestLayer::HttpMethodUnknown; + else if (data[1] == 'R' && data[2] == 'A' && data[3] == 'C' && data[4] == 'E' && data[5] == ' ') + return HttpRequestLayer::HttpTRACE; + else + return HttpRequestLayer::HttpMethodUnknown; + break; + + + case 'H': + if (dataLen < 5) + return HttpRequestLayer::HttpMethodUnknown; + else if (data[1] == 'E' && data[2] == 'A' && data[3] == 'D' && data[4] == ' ') + return HttpRequestLayer::HttpHEAD; + else + return HttpRequestLayer::HttpMethodUnknown; + break; + + case 'O': + if (dataLen < 8) + return HttpRequestLayer::HttpMethodUnknown; + else if (data[1] == 'P' && data[2] == 'T' && data[3] == 'I' && data[4] == 'O' && data[5] == 'N' && data[6] == 'S' && data[7] == ' ') + return HttpRequestLayer::HttpOPTIONS; + else + return HttpRequestLayer::HttpMethodUnknown; + break; + + case 'P': + switch (data[1]) + { + case 'U': + if (data[2] == 'T' && data[3] == ' ') + return HttpRequestLayer::HttpPUT; + else + return HttpRequestLayer::HttpMethodUnknown; + break; + + case 'O': + if (dataLen < 5) + return HttpRequestLayer::HttpMethodUnknown; + else if (data[2] == 'S' && data[3] == 'T' && data[4] == ' ') + return HttpRequestLayer::HttpPOST; + else + return HttpRequestLayer::HttpMethodUnknown; + break; + + case 'A': + if (dataLen < 6) + return HttpRequestLayer::HttpMethodUnknown; + else if (data[2] == 'T' && data[3] == 'C' && data[4] == 'H' && data[5] == ' ') + return HttpRequestLayer::HttpPATCH; + else + return HttpRequestLayer::HttpMethodUnknown; + break; + + default: + return HttpRequestLayer::HttpMethodUnknown; + } + break; + + default: + return HttpRequestLayer::HttpMethodUnknown; + } +} + +void HttpRequestFirstLine::parseVersion() +{ + char* data = (char*)(m_HttpRequest->m_Data + m_UriOffset); + char* verPos = cross_platform_memmem(data, m_HttpRequest->getDataLen() - m_UriOffset, " HTTP/", 6); + if (verPos == nullptr) + { + m_Version = HttpVersionUnknown; + m_VersionOffset = -1; + return; + } + + // verify packet doesn't end before the version, meaning still left place for " HTTP/x.y" (9 chars) + if ((verPos + 9 - (char*)m_HttpRequest->m_Data) > m_HttpRequest->getDataLen()) + { + m_Version = HttpVersionUnknown; + m_VersionOffset = -1; + return; + } + + //skip " HTTP/" (6 chars) + verPos += 6; + switch (verPos[0]) + { + case '0': + if (verPos[1] == '.' && verPos[2] == '9') + m_Version = ZeroDotNine; + else + m_Version = HttpVersionUnknown; + break; + + case '1': + if (verPos[1] == '.' && verPos[2] == '0') + m_Version = OneDotZero; + else if (verPos[1] == '.' && verPos[2] == '1') + m_Version = OneDotOne; + else + m_Version = HttpVersionUnknown; + break; + + default: + m_Version = HttpVersionUnknown; + } + + m_VersionOffset = verPos - (char*)m_HttpRequest->m_Data; +} + +bool HttpRequestFirstLine::setMethod(HttpRequestLayer::HttpMethod newMethod) +{ + if (newMethod == HttpRequestLayer::HttpMethodUnknown) + { + PCPP_LOG_ERROR("Requested method is HttpMethodUnknown"); + return false; + } + + //extend or shorten layer + int lengthDifference = MethodEnumToString[newMethod].length() - MethodEnumToString[m_Method].length(); + if (lengthDifference > 0) + { + if (!m_HttpRequest->extendLayer(0, lengthDifference)) + { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + else if (lengthDifference < 0) + { + if (!m_HttpRequest->shortenLayer(0, 0-lengthDifference)) + { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + + } + } + + if (lengthDifference != 0) + m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), lengthDifference); + + memcpy(m_HttpRequest->m_Data, MethodEnumToString[newMethod].c_str(), MethodEnumToString[newMethod].length()); + + m_Method = newMethod; + m_UriOffset += lengthDifference; + m_VersionOffset += lengthDifference; + + return true; +} + +std::string HttpRequestFirstLine::getUri() const +{ + std::string result; + if (m_UriOffset != -1 && m_VersionOffset != -1) + result.assign((const char*)m_HttpRequest->m_Data + m_UriOffset, m_VersionOffset - 6 - m_UriOffset); + + //else first line is illegal, return empty string + + return result; +} + +bool HttpRequestFirstLine::setUri(std::string newUri) +{ + // make sure the new URI begins with "/" + if (newUri.compare(0, 1, "/") != 0) + newUri = "/" + newUri; + + //extend or shorten layer + std::string currentUri = getUri(); + int lengthDifference = newUri.length() - currentUri.length(); + if (lengthDifference > 0) + { + if (!m_HttpRequest->extendLayer(m_UriOffset, lengthDifference)) + { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + else if (lengthDifference < 0) + { + if (!m_HttpRequest->shortenLayer(m_UriOffset, 0-lengthDifference)) + { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + + if (lengthDifference != 0) + m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), lengthDifference); + + memcpy(m_HttpRequest->m_Data + m_UriOffset, newUri.c_str(), newUri.length()); + + m_VersionOffset += lengthDifference; + + return true; +} + +void HttpRequestFirstLine::setVersion(HttpVersion newVersion) +{ + if (m_VersionOffset == -1) + return; + + if (newVersion == HttpVersionUnknown) + return; + + char* verPos = (char*)(m_HttpRequest->m_Data + m_VersionOffset); + memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3); + + m_Version = newVersion; +} + + + + + + +// -------- Class HttpResponseLayer ----------------- + + + +const std::string StatusCodeEnumToString[80] = { + "Continue", + "Switching Protocols", + "Processing", + "OK", + "Created", + "Accepted", + "Non-Authoritative Information", + "No Content", + "Reset Content", + "Partial Content", + "Multi-Status", + "Already Reported", + "IM Used", + "Multiple Choices", + "Moved Permanently", + "Found", + "See Other", + "Not Modified", + "Use Proxy", + "Switch Proxy", + "Temporary Redirect", + "Permanent Redirect", + "Bad Request", + "Unauthorized", + "Payment Required", + "Forbidden", + "Not Found", + "Method Not Allowed", + "Not Acceptable", + "Proxy Authentication Required", + "Request Timeout", + "Conflict", + "Gone", + "Length Required", + "Precondition Failed", + "Request Entity Too Large", + "Request-URI Too Long", + "Unsupported Media Type", + "Requested Range Not Satisfiable", + "Expectation Failed", + "I'm a teapot", + "Authentication Timeout", + "Method Failure", + "Unprocessable Entity", + "Locked", + "Failed Dependency", + "Upgrade Required", + "Precondition Required", + "Too Many Requests", + "Request Header Fields Too Large", + "Login Timeout", + "No Response", + "Retry With", + "Blocked by Windows Parental Controls", + "Unavailable For Legal Reasons", + "Request Header Too Large", + "Cert Error", + "No Cert", + "HTTP to HTTPS", + "Token expired/invalid", + "Client Closed Request", + "Internal Server Error", + "Not Implemented", + "Bad Gateway", + "Service Unavailable", + "Gateway Timeout", + "HTTP Version Not Supported", + "Variant Also Negotiates", + "Insufficient Storage", + "Loop Detected", + "Bandwidth Limit Exceeded", + "Not Extended", + "Network Authentication Required", + "Origin Error", + "Web server is down", + "Connection timed out", + "Proxy Declined Request", + "A timeout occurred", + "Network read timeout error", + "Network connect timeout error" +}; + + +const int StatusCodeEnumToInt[80] = { + 100, + 101, + 102, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 226, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 422, + 423, + 424, + 426, + 428, + 429, + 431, + 440, + 444, + 449, + 450, + 451, + 494, + 495, + 496, + 497, + 498, + 499, + 500, + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 520, + 521, + 522, + 523, + 524, + 598, + 599 +}; + + + +HttpResponseLayer::HttpResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : HttpMessage(data, dataLen, prevLayer, packet) +{ + m_Protocol = HTTPResponse; + m_FirstLine = new HttpResponseFirstLine(this); + m_FieldsOffset = m_FirstLine->getSize(); + parseFields(); +} + +HttpResponseLayer::HttpResponseLayer(HttpVersion version, HttpResponseLayer::HttpResponseStatusCode statusCode, std::string statusCodeString) +{ + m_Protocol = HTTPResponse; + m_FirstLine = new HttpResponseFirstLine(this, version, statusCode, std::move(statusCodeString)); + m_FieldsOffset = m_FirstLine->getSize(); +} + +HttpResponseLayer::~HttpResponseLayer() +{ + delete m_FirstLine; +} + + +HttpResponseLayer::HttpResponseLayer(const HttpResponseLayer& other) : HttpMessage(other) +{ + m_FirstLine = new HttpResponseFirstLine(this); +} + +HttpResponseLayer& HttpResponseLayer::operator=(const HttpResponseLayer& other) +{ + HttpMessage::operator=(other); + + if (m_FirstLine != nullptr) + delete m_FirstLine; + + m_FirstLine = new HttpResponseFirstLine(this); + + return *this; +} + + +HeaderField* HttpResponseLayer::setContentLength(int contentLength, const std::string &prevFieldName) +{ + std::ostringstream contentLengthAsString; + contentLengthAsString << contentLength; + std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD); + HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); + if (contentLengthField == nullptr) + { + HeaderField* prevField = getFieldByName(prevFieldName); + contentLengthField = insertField(prevField, PCPP_HTTP_CONTENT_LENGTH_FIELD, contentLengthAsString.str()); + } + else + contentLengthField->setFieldValue(contentLengthAsString.str()); + + return contentLengthField; +} + +int HttpResponseLayer::getContentLength() const +{ + std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD); + std::transform(contentLengthFieldName.begin(), contentLengthFieldName.end(), contentLengthFieldName.begin(), ::tolower); + HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); + if (contentLengthField != nullptr) + return atoi(contentLengthField->getFieldValue().c_str()); + return 0; +} + +std::string HttpResponseLayer::toString() const +{ + static const int maxLengthToPrint = 120; + std::string result = "HTTP response, "; + int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line + if (size <= maxLengthToPrint) + { + char* firstLine = new char[size+1]; + strncpy(firstLine, (char*)m_Data, size); + firstLine[size] = 0; + result += std::string(firstLine); + delete[] firstLine; + } + else + { + char firstLine[maxLengthToPrint+1]; + strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3); + firstLine[maxLengthToPrint-3] = '.'; + firstLine[maxLengthToPrint-2] = '.'; + firstLine[maxLengthToPrint-1] = '.'; + firstLine[maxLengthToPrint] = 0; + result += std::string(firstLine); + } + + return result; +} + + + + + + + + +// -------- Class HttpResponseFirstLine ----------------- + + + +int HttpResponseFirstLine::getStatusCodeAsInt() const +{ + return StatusCodeEnumToInt[m_StatusCode]; +} + +std::string HttpResponseFirstLine::getStatusCodeString() const +{ + std::string result; + const int statusStringOffset = 13; + if (m_StatusCode != HttpResponseLayer::HttpStatusCodeUnknown) + { + int statusStringEndOffset = m_FirstLineEndOffset - 2; + if ((*(m_HttpResponse->m_Data + statusStringEndOffset)) != '\r') + statusStringEndOffset++; + result.assign((char*)(m_HttpResponse->m_Data + statusStringOffset), statusStringEndOffset-statusStringOffset); + } + + //else first line is illegal, return empty string + + return result; +} + +bool HttpResponseFirstLine::setStatusCode(HttpResponseLayer::HttpResponseStatusCode newStatusCode, std::string statusCodeString) +{ + if (newStatusCode == HttpResponseLayer::HttpStatusCodeUnknown) + { + PCPP_LOG_ERROR("Requested status code is HttpStatusCodeUnknown"); + return false; + } + + //extend or shorten layer + + size_t statusStringOffset = 13; + if (statusCodeString == "") + statusCodeString = StatusCodeEnumToString[newStatusCode]; + int lengthDifference = statusCodeString.length() - getStatusCodeString().length(); + if (lengthDifference > 0) + { + if (!m_HttpResponse->extendLayer(statusStringOffset, lengthDifference)) + { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + else if (lengthDifference < 0) + { + if (!m_HttpResponse->shortenLayer(statusStringOffset, 0-lengthDifference)) + { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + + } + } + + if (lengthDifference != 0) + m_HttpResponse->shiftFieldsOffset(m_HttpResponse->getFirstField(), lengthDifference); + + // copy status string + memcpy(m_HttpResponse->m_Data+statusStringOffset, statusCodeString.c_str(), statusCodeString.length()); + + // change status code + std::ostringstream statusCodeAsString; + statusCodeAsString << StatusCodeEnumToInt[newStatusCode]; + memcpy(m_HttpResponse->m_Data+9, statusCodeAsString.str().c_str(), 3); + + m_StatusCode = newStatusCode; + + m_FirstLineEndOffset += lengthDifference; + + return true; + +} + +void HttpResponseFirstLine::setVersion(HttpVersion newVersion) +{ + if (newVersion == HttpVersionUnknown) + return; + + char* verPos = (char*)(m_HttpResponse->m_Data + 5); + memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3); + + m_Version = newVersion; +} + +HttpResponseLayer::HttpResponseStatusCode HttpResponseFirstLine::validateStatusCode(char* data, size_t dataLen, HttpResponseLayer::HttpResponseStatusCode potentialCode) +{ + if (dataLen < 1 || data[0] != ' ') + return HttpResponseLayer::HttpStatusCodeUnknown; + + return potentialCode; +} + +HttpResponseLayer::HttpResponseStatusCode HttpResponseFirstLine::parseStatusCode(char* data, size_t dataLen) +{ + if (parseVersion(data, dataLen) == HttpVersionUnknown) + return HttpResponseLayer::HttpStatusCodeUnknown; + + // minimum data should be 12B long: "HTTP/x.y XXX" + if (dataLen < 12) + return HttpResponseLayer::HttpStatusCodeUnknown; + + char* statusCodeData = data + 9; + size_t statusCodeDataLen = dataLen - 9; + + switch (statusCodeData[0]) + { + case '1': + switch (statusCodeData[1]) + { + case '0': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http100Continue); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http101SwitchingProtocols); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http102Processing); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + }; + + break; + + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + }; + + break; + case '2': + switch (statusCodeData[1]) + { + case '0': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http200OK); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http201Created); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http202Accepted); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http203NonAuthoritativeInformation); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http204NoContent); + case '5': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http205ResetContent); + case '6': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http206PartialContent); + case '7': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http207MultiStatus); + case '8': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http208AlreadyReported); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + + }; + + break; + case '2': + switch (statusCodeData[2]) + { + case '6': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http226IMUsed); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + }; + + break; + + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + + }; + + break; + + case '3': + switch (statusCodeData[1]) + { + case '0': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http300MultipleChoices); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http301MovedPermanently); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http302); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http303SeeOther); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http304NotModified); + case '5': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http305UseProxy); + case '6': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http306SwitchProxy); + case '7': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http307TemporaryRedirect); + case '8': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http308PermanentRedirect); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + + }; + + break; + + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + }; + + break; + + case '4': + switch (statusCodeData[1]) + { + case '0': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http400BadRequest); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http401Unauthorized); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http402PaymentRequired); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http403Forbidden); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http404NotFound); + case '5': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http405MethodNotAllowed); + case '6': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http406NotAcceptable); + case '7': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http407ProxyAuthenticationRequired); + case '8': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http408RequestTimeout); + case '9': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http409Conflict); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + + }; + + break; + + case '1': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http410Gone); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http411LengthRequired); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http412PreconditionFailed); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http413RequestEntityTooLarge); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http414RequestURITooLong); + case '5': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http415UnsupportedMediaType); + case '6': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http416RequestedRangeNotSatisfiable); + case '7': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http417ExpectationFailed); + case '8': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http418Imateapot); + case '9': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http419AuthenticationTimeout); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + + }; + + break; + + case '2': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http420); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http422UnprocessableEntity); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http423Locked); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http424FailedDependency); + case '6': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http426UpgradeRequired); + case '8': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http428PreconditionRequired); + case '9': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http429TooManyRequests); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + + }; + + break; + + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http431RequestHeaderFieldsTooLarge); + + case '4': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http440LoginTimeout); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http444NoResponse); + case '9': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http449RetryWith); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + }; + + break; + + case '5': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http450BlockedByWindowsParentalControls); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http451); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + }; + + break; + + case '9': + switch (statusCodeData[2]) + { + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http494RequestHeaderTooLarge); + case '5': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http495CertError); + case '6': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http496NoCert); + case '7': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http497HTTPtoHTTPS); + case '8': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http498TokenExpiredInvalid); + case '9': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http499); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + }; + + break; + + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + }; + + break; + + case '5': + switch (statusCodeData[1]) + { + case '0': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http500InternalServerError); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http501NotImplemented); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http502BadGateway); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http503ServiceUnavailable); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http504GatewayTimeout); + case '5': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http505HTTPVersionNotSupported); + case '6': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http506VariantAlsoNegotiates); + case '7': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http507InsufficientStorage); + case '8': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http508LoopDetected); + case '9': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http509BandwidthLimitExceeded); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + + }; + + break; + + case '1': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http510NotExtended); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http511NetworkAuthenticationRequired); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + }; + + break; + + case '2': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http520OriginError); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http521WebServerIsDown); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http522ConnectionTimedOut); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http523ProxyDeclinedRequest); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http524aTimeoutOccurred); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + }; + + break; + + case '9': + switch (statusCodeData[2]) + { + case '8': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http598NetworkReadTimeoutError); + case '9': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http599NetworkConnectTimeoutError); + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + }; + + break; + + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + }; + + break; + + default: + return HttpResponseLayer::HttpStatusCodeUnknown; + } + + return HttpResponseLayer::HttpStatusCodeUnknown; +} + +HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse) : m_HttpResponse(httpResponse) +{ + m_Version = parseVersion((char*)m_HttpResponse->m_Data, m_HttpResponse->getDataLen()); + if (m_Version == HttpVersionUnknown) + { + m_StatusCode = HttpResponseLayer::HttpStatusCodeUnknown; + } + else + { + m_StatusCode = parseStatusCode((char*)m_HttpResponse->m_Data, m_HttpResponse->getDataLen()); + } + + + char* endOfFirstLine; + if ((endOfFirstLine = (char*)memchr((char*)(m_HttpResponse->m_Data), '\n', m_HttpResponse->m_DataLen)) != nullptr) + { + m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpResponse->m_Data + 1; + m_IsComplete = true; + } + else + { + m_FirstLineEndOffset = m_HttpResponse->getDataLen(); + m_IsComplete = false; + } + + if (Logger::getInstance().isDebugEnabled(PacketLogModuleHttpLayer)) + { + std::string version = (m_Version == HttpVersionUnknown ? "Unknown" : VersionEnumToString[m_Version]); + int statusCode = (m_StatusCode == HttpResponseLayer::HttpStatusCodeUnknown ? 0 : StatusCodeEnumToInt[m_StatusCode]); + PCPP_LOG_DEBUG("Version='" << version << "'; Status code=" << statusCode << " '" << getStatusCodeString() << "'"); + } +} + + +HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse, HttpVersion version, HttpResponseLayer::HttpResponseStatusCode statusCode, std::string statusCodeString) +{ + if (statusCode == HttpResponseLayer::HttpStatusCodeUnknown) + { + m_Exception.setMessage("Status code supplied was HttpStatusCodeUnknown"); + throw m_Exception; + } + + if (version == HttpVersionUnknown) + { + m_Exception.setMessage("Version supplied was HttpVersionUnknown"); + throw m_Exception; + } + + m_HttpResponse = httpResponse; + + m_StatusCode = statusCode; + m_Version = version; + + std::ostringstream statusCodeAsString; + statusCodeAsString << StatusCodeEnumToInt[m_StatusCode]; + if (statusCodeString == "") + statusCodeString = StatusCodeEnumToString[m_StatusCode]; + std::string firstLine = "HTTP/" + VersionEnumToString[m_Version] + " " + statusCodeAsString.str() + " " + statusCodeString + "\r\n"; + + m_FirstLineEndOffset = firstLine.length(); + + m_HttpResponse->m_DataLen = firstLine.length(); + m_HttpResponse->m_Data = new uint8_t[m_HttpResponse->m_DataLen]; + memcpy(m_HttpResponse->m_Data, firstLine.c_str(), m_HttpResponse->m_DataLen); + + m_IsComplete = true; +} + +HttpVersion HttpResponseFirstLine::parseVersion(char* data, size_t dataLen) +{ + if (dataLen < 8) // "HTTP/x.y" + { + PCPP_LOG_DEBUG("HTTP response length < 8, cannot identify version"); + return HttpVersionUnknown; + } + + if (data[0] != 'H' || data[1] != 'T' || data[2] != 'T' || data[3] != 'P' || data[4] != '/') + { + PCPP_LOG_DEBUG("HTTP response does not begin with 'HTTP/'"); + return HttpVersionUnknown; + } + + char* verPos = data + 5; + switch (verPos[0]) + { + case '0': + if (verPos[1] == '.' && verPos[2] == '9') + return ZeroDotNine; + else + return HttpVersionUnknown; + break; + + case '1': + if (verPos[1] == '.' && verPos[2] == '0') + return OneDotZero; + else if (verPos[1] == '.' && verPos[2] == '1') + return OneDotOne; + else + return HttpVersionUnknown; + break; + + default: + return HttpVersionUnknown; + } +} + +} // namespace pcpp diff --git a/Packet++/src/IPv4Layer.cpp b/Packet++/src/IPv4Layer.cpp index a2addedc60..ef2a528ee1 100644 --- a/Packet++/src/IPv4Layer.cpp +++ b/Packet++/src/IPv4Layer.cpp @@ -1,580 +1,580 @@ -#define LOG_MODULE PacketLogModuleIPv4Layer - -#include "IPv4Layer.h" -#include "IPv6Layer.h" -#include "PayloadLayer.h" -#include "UdpLayer.h" -#include "TcpLayer.h" -#include "IcmpLayer.h" -#include "GreLayer.h" -#include "IgmpLayer.h" -#include "IPSecLayer.h" -#include "PacketUtils.h" -#include -#include -#include "Logger.h" -#include "EndianPortable.h" - -namespace pcpp -{ - -#define IPV4OPT_DUMMY 0xff -#define IPV4_MAX_OPT_SIZE 40 - - -/// ~~~~~~~~~~~~~~~~~ -/// IPv4OptionBuilder -/// ~~~~~~~~~~~~~~~~~ - -IPv4OptionBuilder::IPv4OptionBuilder(IPv4OptionTypes optionType, const std::vector& ipList) -{ - m_RecType = (uint8_t)optionType; - m_RecValueLen = ipList.size() * sizeof(uint32_t) + sizeof(uint8_t); - m_RecValue = new uint8_t[m_RecValueLen]; - - size_t curOffset = 0; - m_RecValue[curOffset++] = 0; // init pointer value - - bool firstZero = false; - for (std::vector::const_iterator iter = ipList.begin(); iter != ipList.end(); iter++) - { - uint32_t ipAddrAsInt = iter->toInt(); - - if (!firstZero) - m_RecValue[0] += (uint8_t)4; - - if (!firstZero && ipAddrAsInt == 0) - firstZero = true; - - memcpy(m_RecValue + curOffset, &ipAddrAsInt, sizeof(uint32_t)); - curOffset += sizeof(uint32_t); - } - - m_BuilderParamsValid = true; -} - -IPv4OptionBuilder::IPv4OptionBuilder(const IPv4TimestampOptionValue& timestampValue) -{ - m_RecType = (uint8_t)IPV4OPT_Timestamp; - m_RecValueLen = 0; - m_RecValue = nullptr; - - if (timestampValue.type == IPv4TimestampOptionValue::Unknown) - { - PCPP_LOG_ERROR("Cannot build timestamp option of type IPv4TimestampOptionValue::Unknown"); - m_BuilderParamsValid = false; - return; - } - - if (timestampValue.type == IPv4TimestampOptionValue::TimestampsForPrespecifiedIPs) - { - PCPP_LOG_ERROR("Cannot build timestamp option of type IPv4TimestampOptionValue::TimestampsForPrespecifiedIPs - this type is not supported"); - m_BuilderParamsValid = false; - return; - } - - if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP && timestampValue.timestamps.size() != timestampValue.ipAddresses.size()) - { - PCPP_LOG_ERROR("Cannot build timestamp option of type IPv4TimestampOptionValue::TimestampAndIP because number of timestamps and IP addresses is not equal"); - m_BuilderParamsValid = false; - return; - } - - m_RecValueLen = timestampValue.timestamps.size() * sizeof(uint32_t) + 2 * sizeof(uint8_t); - - if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP) - { - m_RecValueLen += timestampValue.timestamps.size() * sizeof(uint32_t); - } - - m_RecValue = new uint8_t[m_RecValueLen]; - - size_t curOffset = 0; - m_RecValue[curOffset++] = 1; //pointer default value is 1 - means there are no empty timestamps - m_RecValue[curOffset++] = (uint8_t)timestampValue.type; // timestamp type - - int firstZero = -1; - for (int i = 0; i < (int)timestampValue.timestamps.size(); i++) - { - uint32_t timestamp = htobe32(timestampValue.timestamps.at(i)); - - // for pointer calculation - find the first timestamp equals to 0 - if (timestamp == 0 && firstZero == -1) - firstZero = i; - - if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP) - { - uint32_t ipAddrAsInt = timestampValue.ipAddresses.at(i).toInt(); - memcpy(m_RecValue + curOffset , &ipAddrAsInt, sizeof(uint32_t)); - curOffset += sizeof(uint32_t); - } - - memcpy(m_RecValue + curOffset , ×tamp, sizeof(uint32_t)); - curOffset += sizeof(uint32_t); - } - - // calculate pointer field - if (firstZero > -1) - { - uint8_t pointerVal = (uint8_t)(4 * sizeof(uint8_t) + firstZero * sizeof(uint32_t) + 1); - if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP) - pointerVal += (uint8_t)(firstZero * sizeof(uint32_t)); - - m_RecValue[0] = pointerVal; - } - - m_BuilderParamsValid = true; -} - -IPv4Option IPv4OptionBuilder::build() const -{ - if (!m_BuilderParamsValid) - return IPv4Option(nullptr); - - size_t optionSize = m_RecValueLen + 2 * sizeof(uint8_t); - - uint8_t recType = static_cast(m_RecType); - if ((recType == (uint8_t)IPV4OPT_NOP || recType == (uint8_t)IPV4OPT_EndOfOptionsList)) - { - if (m_RecValueLen != 0) - { - PCPP_LOG_ERROR("Can't set IPv4 NOP option or IPv4 End-of-options option with size different than 0, tried to set size " << (int)m_RecValueLen); - return IPv4Option(nullptr); - } - - optionSize = sizeof(uint8_t); - } - - uint8_t* recordBuffer = new uint8_t[optionSize]; - memset(recordBuffer, 0, optionSize); - recordBuffer[0] = recType; - if (optionSize > 1) - { - recordBuffer[1] = static_cast(optionSize); - if (optionSize > 2 && m_RecValue != nullptr) - memcpy(recordBuffer + 2, m_RecValue, m_RecValueLen); - } - - return IPv4Option(recordBuffer); -} - - -/// ~~~~~~~~~ -/// IPv4Layer -/// ~~~~~~~~~ - - -void IPv4Layer::initLayer() -{ - const size_t headerLen = sizeof(iphdr); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - m_Protocol = IPv4; - memset(m_Data, 0, headerLen); - iphdr* ipHdr = getIPv4Header(); - ipHdr->internetHeaderLength = (5 & 0xf); - m_NumOfTrailingBytes = 0; - m_TempHeaderExtension = 0; -} - -void IPv4Layer::initLayerInPacket(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, bool setTotalLenAsDataLen) -{ - m_Protocol = IPv4; - m_NumOfTrailingBytes = 0; - m_TempHeaderExtension = 0; - if (setTotalLenAsDataLen) - { - size_t totalLen = be16toh(getIPv4Header()->totalLength); - // if totalLen == 0 this usually means TCP Segmentation Offload (TSO). In this case we should ignore the value of totalLen - // and look at the data captured on the wire - if ((totalLen < m_DataLen) && (totalLen !=0)) - m_DataLen = totalLen; - } -} - -void IPv4Layer::copyLayerData(const IPv4Layer& other) -{ - m_OptionReader = other.m_OptionReader; - m_NumOfTrailingBytes = other.m_NumOfTrailingBytes; - m_TempHeaderExtension = other.m_TempHeaderExtension; -} - -IPv4Layer::IPv4Layer() -{ - initLayer(); -} - -IPv4Layer::IPv4Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, bool setTotalLenAsDataLen) : Layer(data, dataLen, prevLayer, packet) -{ - initLayerInPacket(data, dataLen, prevLayer, packet, setTotalLenAsDataLen); -} - -IPv4Layer::IPv4Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) -{ - initLayerInPacket(data, dataLen, prevLayer, packet, true); -} - -IPv4Layer::IPv4Layer(const IPv4Address& srcIP, const IPv4Address& dstIP) -{ - initLayer(); - iphdr* ipHdr = getIPv4Header(); - ipHdr->ipSrc = srcIP.toInt(); - ipHdr->ipDst = dstIP.toInt(); -} - -IPv4Layer::IPv4Layer(const IPv4Layer& other) : Layer(other) -{ - copyLayerData(other); -} - -IPv4Layer& IPv4Layer::operator=(const IPv4Layer& other) -{ - Layer::operator=(other); - - copyLayerData(other); - - return *this; -} - -void IPv4Layer::parseNextLayer() -{ - size_t hdrLen = getHeaderLen(); - if (m_DataLen <= hdrLen || hdrLen == 0) - return; - - iphdr* ipHdr = getIPv4Header(); - - ProtocolType greVer = UnknownProtocol; - ProtocolType igmpVer = UnknownProtocol; - bool igmpQuery = false; - - uint8_t ipVersion = 0; - - uint8_t* payload = m_Data + hdrLen; - size_t payloadLen = m_DataLen - hdrLen; - - // If it's a fragment don't parse upper layers, unless if it's the first fragment - // TODO: assuming first fragment contains at least L4 header, what if it's not true? - if (isFragment()) - { - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - return; - } - - switch (ipHdr->protocol) - { - case PACKETPP_IPPROTO_UDP: - if (payloadLen >= sizeof(udphdr)) - m_NextLayer = new UdpLayer(payload, payloadLen, this, m_Packet); - break; - case PACKETPP_IPPROTO_TCP: - m_NextLayer = TcpLayer::isDataValid(payload, payloadLen) - ? static_cast(new TcpLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PACKETPP_IPPROTO_ICMP: - m_NextLayer = IcmpLayer::isDataValid(payload, payloadLen) - ? static_cast(new IcmpLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PACKETPP_IPPROTO_IPIP: - ipVersion = *payload >> 4; - if (ipVersion == 4 && IPv4Layer::isDataValid(payload, payloadLen)) - m_NextLayer = new IPv4Layer(payload, payloadLen, this, m_Packet); - else if (ipVersion == 6 && IPv6Layer::isDataValid(payload, payloadLen)) - m_NextLayer = new IPv6Layer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - case PACKETPP_IPPROTO_GRE: - greVer = GreLayer::getGREVersion(payload, payloadLen); - if (greVer == GREv0) - m_NextLayer = new GREv0Layer(payload, payloadLen, this, m_Packet); - else if (greVer == GREv1) - m_NextLayer = new GREv1Layer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - case PACKETPP_IPPROTO_IGMP: - igmpVer = IgmpLayer::getIGMPVerFromData( - payload, std::min(payloadLen, be16toh(getIPv4Header()->totalLength) - hdrLen), igmpQuery); - if (igmpVer == IGMPv1) - m_NextLayer = new IgmpV1Layer(payload, payloadLen, this, m_Packet); - else if (igmpVer == IGMPv2) - m_NextLayer = new IgmpV2Layer(payload, payloadLen, this, m_Packet); - else if (igmpVer == IGMPv3) - { - if (igmpQuery) - m_NextLayer = new IgmpV3QueryLayer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new IgmpV3ReportLayer(payload, payloadLen, this, m_Packet); - } - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - break; - case PACKETPP_IPPROTO_AH: - m_NextLayer = AuthenticationHeaderLayer::isDataValid(payload, payloadLen) - ? static_cast(new AuthenticationHeaderLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PACKETPP_IPPROTO_ESP: - m_NextLayer = ESPLayer::isDataValid(payload, payloadLen) - ? static_cast(new ESPLayer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PACKETPP_IPPROTO_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } -} - -void IPv4Layer::computeCalculateFields() -{ - iphdr* ipHdr = getIPv4Header(); - ipHdr->ipVersion = (4 & 0x0f); - ipHdr->totalLength = htobe16(m_DataLen); - ipHdr->headerChecksum = 0; - - if (m_NextLayer != nullptr) - { - switch (m_NextLayer->getProtocol()) - { - case TCP: - ipHdr->protocol = PACKETPP_IPPROTO_TCP; - break; - case UDP: - ipHdr->protocol = PACKETPP_IPPROTO_UDP; - break; - case ICMP: - ipHdr->protocol = PACKETPP_IPPROTO_ICMP; - break; - case GREv0: - case GREv1: - ipHdr->protocol = PACKETPP_IPPROTO_GRE; - break; - case IGMPv1: - case IGMPv2: - case IGMPv3: - ipHdr->protocol = PACKETPP_IPPROTO_IGMP; - break; - default: - break; - } - } - - ScalarBuffer scalar = { (uint16_t*)ipHdr, (size_t)(ipHdr->internetHeaderLength*4) } ; - ipHdr->headerChecksum = htobe16(computeChecksum(&scalar, 1)); -} - -bool IPv4Layer::isFragment() const -{ - return ((getFragmentFlags() & PCPP_IP_MORE_FRAGMENTS) != 0 || getFragmentOffset() != 0); -} - -bool IPv4Layer::isFirstFragment() const -{ - return isFragment() && (getFragmentOffset() == 0); -} - -bool IPv4Layer::isLastFragment() const -{ - return isFragment() && ((getFragmentFlags() & PCPP_IP_MORE_FRAGMENTS) == 0); -} - -uint8_t IPv4Layer::getFragmentFlags() const -{ - return getIPv4Header()->fragmentOffset & 0xE0; -} - -uint16_t IPv4Layer::getFragmentOffset() const -{ - return be16toh(getIPv4Header()->fragmentOffset & (uint16_t)0xFF1F) * 8; -} - -std::string IPv4Layer::toString() const -{ - std::string fragment = ""; - if (isFragment()) - { - if (isFirstFragment()) - fragment = "First fragment"; - else if (isLastFragment()) - fragment = "Last fragment"; - else - fragment = "Fragment"; - - std::stringstream sstm; - sstm << fragment << " [offset= " << getFragmentOffset() << "], "; - fragment = sstm.str(); - } - - return "IPv4 Layer, " + fragment + "Src: " + getSrcIPv4Address().toString() + ", Dst: " + getDstIPv4Address().toString(); -} - -IPv4Option IPv4Layer::getOption(IPv4OptionTypes option) const -{ - return m_OptionReader.getTLVRecord((uint8_t)option, getOptionsBasePtr(), getHeaderLen() - sizeof(iphdr)); -} - -IPv4Option IPv4Layer::getFirstOption() const -{ - return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), getHeaderLen() - sizeof(iphdr)); -} - -IPv4Option IPv4Layer::getNextOption(IPv4Option& option) const -{ - return m_OptionReader.getNextTLVRecord(option, getOptionsBasePtr(), getHeaderLen() - sizeof(iphdr)); -} - -size_t IPv4Layer::getOptionCount() const -{ - return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), getHeaderLen() - sizeof(iphdr)); -} - -void IPv4Layer::adjustOptionsTrailer(size_t totalOptSize) -{ - size_t ipHdrSize = sizeof(iphdr); - - int newNumberOfTrailingBytes = 0; - while ((totalOptSize + newNumberOfTrailingBytes) % 4 != 0) - newNumberOfTrailingBytes++; - - if (newNumberOfTrailingBytes < m_NumOfTrailingBytes) - shortenLayer(ipHdrSize+totalOptSize, m_NumOfTrailingBytes - newNumberOfTrailingBytes); - else if (newNumberOfTrailingBytes > m_NumOfTrailingBytes) - extendLayer(ipHdrSize+totalOptSize, newNumberOfTrailingBytes - m_NumOfTrailingBytes); - - m_NumOfTrailingBytes = newNumberOfTrailingBytes; - - for (int i = 0; i < m_NumOfTrailingBytes; i++) - m_Data[ipHdrSize + totalOptSize + i] = IPV4OPT_DUMMY; - - m_TempHeaderExtension = 0; - getIPv4Header()->internetHeaderLength = ((ipHdrSize + totalOptSize + m_NumOfTrailingBytes)/4 & 0x0f); -} - -IPv4Option IPv4Layer::addOptionAt(const IPv4OptionBuilder& optionBuilder, int offset) -{ - IPv4Option newOption = optionBuilder.build(); - if (newOption.isNull()) - return newOption; - - size_t sizeToExtend = newOption.getTotalSize(); - - size_t totalOptSize = getHeaderLen() - sizeof(iphdr) - m_NumOfTrailingBytes + sizeToExtend; - - if (totalOptSize > IPV4_MAX_OPT_SIZE) - { - PCPP_LOG_ERROR("Cannot add option - adding this option will exceed IPv4 total option size which is " << IPV4_MAX_OPT_SIZE); - newOption.purgeRecordData(); - return IPv4Option(nullptr); - } - - if (!extendLayer(offset, sizeToExtend)) - { - PCPP_LOG_ERROR("Could not extend IPv4Layer in [" << sizeToExtend << "] bytes"); - newOption.purgeRecordData(); - return IPv4Option(nullptr); - } - - memcpy(m_Data + offset, newOption.getRecordBasePtr(), newOption.getTotalSize()); - - newOption.purgeRecordData(); - - // setting this m_TempHeaderExtension because adjustOptionsTrailer() may extend or shorten the layer and the extend or shorten methods need to know the accurate - // current size of the header. m_TempHeaderExtension will be added to the length extracted from getIPv4Header()->internetHeaderLength as the temp new size - m_TempHeaderExtension = sizeToExtend; - adjustOptionsTrailer(totalOptSize); - // the adjustOptionsTrailer() adds or removed the trailing bytes and sets getIPv4Header()->internetHeaderLength to the correct size, so the m_TempHeaderExtension - // isn't needed anymore - m_TempHeaderExtension = 0; - - m_OptionReader.changeTLVRecordCount(1); - - uint8_t* newOptPtr = m_Data + offset; - - return IPv4Option(newOptPtr); -} - -IPv4Option IPv4Layer::addOption(const IPv4OptionBuilder& optionBuilder) -{ - return addOptionAt(optionBuilder, getHeaderLen() - m_NumOfTrailingBytes); -} - -IPv4Option IPv4Layer::addOptionAfter(const IPv4OptionBuilder& optionBuilder, IPv4OptionTypes prevOptionType) -{ - int offset = 0; - - IPv4Option prevOpt = getOption(prevOptionType); - - if (prevOpt.isNull()) - { - offset = sizeof(iphdr); - } - else - { - offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; - } - - return addOptionAt(optionBuilder, offset); -} - -bool IPv4Layer::removeOption(IPv4OptionTypes option) -{ - IPv4Option opt = getOption(option); - if (opt.isNull()) - { - return false; - } - - // calculate total option size - IPv4Option curOpt = getFirstOption(); - size_t totalOptSize = 0; - while (!curOpt.isNull()) - { - totalOptSize += curOpt.getTotalSize(); - curOpt = getNextOption(curOpt); - } - totalOptSize -= opt.getTotalSize(); - - - int offset = opt.getRecordBasePtr() - m_Data; - - size_t sizeToShorten = opt.getTotalSize(); - if (!shortenLayer(offset, sizeToShorten)) - { - PCPP_LOG_ERROR("Failed to remove IPv4 option: cannot shorten layer"); - return false; - } - - // setting this m_TempHeaderExtension because adjustOptionsTrailer() may extend or shorten the layer and the extend or shorten methods need to know the accurate - // current size of the header. m_TempHeaderExtension will be added to the length extracted from getIPv4Header()->internetHeaderLength as the temp new size - m_TempHeaderExtension = 0 - sizeToShorten; - adjustOptionsTrailer(totalOptSize); - // the adjustOptionsTrailer() adds or removed the trailing bytes and sets getIPv4Header()->internetHeaderLength to the correct size, so the m_TempHeaderExtension - // isn't needed anymore - m_TempHeaderExtension = 0; - - m_OptionReader.changeTLVRecordCount(-1); - - return true; -} - -bool IPv4Layer::removeAllOptions() -{ - int offset = sizeof(iphdr); - - if (!shortenLayer(offset, getHeaderLen() - offset)) - return false; - - getIPv4Header()->internetHeaderLength = (5 & 0xf); - m_NumOfTrailingBytes = 0; - m_OptionReader.changeTLVRecordCount(0 - getOptionCount()); - return true; -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModuleIPv4Layer + +#include "IPv4Layer.h" +#include "IPv6Layer.h" +#include "PayloadLayer.h" +#include "UdpLayer.h" +#include "TcpLayer.h" +#include "IcmpLayer.h" +#include "GreLayer.h" +#include "IgmpLayer.h" +#include "IPSecLayer.h" +#include "PacketUtils.h" +#include +#include +#include "Logger.h" +#include "EndianPortable.h" + +namespace pcpp +{ + +#define IPV4OPT_DUMMY 0xff +#define IPV4_MAX_OPT_SIZE 40 + + +/// ~~~~~~~~~~~~~~~~~ +/// IPv4OptionBuilder +/// ~~~~~~~~~~~~~~~~~ + +IPv4OptionBuilder::IPv4OptionBuilder(IPv4OptionTypes optionType, const std::vector& ipList) +{ + m_RecType = (uint8_t)optionType; + m_RecValueLen = ipList.size() * sizeof(uint32_t) + sizeof(uint8_t); + m_RecValue = new uint8_t[m_RecValueLen]; + + size_t curOffset = 0; + m_RecValue[curOffset++] = 0; // init pointer value + + bool firstZero = false; + for (std::vector::const_iterator iter = ipList.begin(); iter != ipList.end(); iter++) + { + uint32_t ipAddrAsInt = iter->toInt(); + + if (!firstZero) + m_RecValue[0] += (uint8_t)4; + + if (!firstZero && ipAddrAsInt == 0) + firstZero = true; + + memcpy(m_RecValue + curOffset, &ipAddrAsInt, sizeof(uint32_t)); + curOffset += sizeof(uint32_t); + } + + m_BuilderParamsValid = true; +} + +IPv4OptionBuilder::IPv4OptionBuilder(const IPv4TimestampOptionValue& timestampValue) +{ + m_RecType = (uint8_t)IPV4OPT_Timestamp; + m_RecValueLen = 0; + m_RecValue = nullptr; + + if (timestampValue.type == IPv4TimestampOptionValue::Unknown) + { + PCPP_LOG_ERROR("Cannot build timestamp option of type IPv4TimestampOptionValue::Unknown"); + m_BuilderParamsValid = false; + return; + } + + if (timestampValue.type == IPv4TimestampOptionValue::TimestampsForPrespecifiedIPs) + { + PCPP_LOG_ERROR("Cannot build timestamp option of type IPv4TimestampOptionValue::TimestampsForPrespecifiedIPs - this type is not supported"); + m_BuilderParamsValid = false; + return; + } + + if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP && timestampValue.timestamps.size() != timestampValue.ipAddresses.size()) + { + PCPP_LOG_ERROR("Cannot build timestamp option of type IPv4TimestampOptionValue::TimestampAndIP because number of timestamps and IP addresses is not equal"); + m_BuilderParamsValid = false; + return; + } + + m_RecValueLen = timestampValue.timestamps.size() * sizeof(uint32_t) + 2 * sizeof(uint8_t); + + if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP) + { + m_RecValueLen += timestampValue.timestamps.size() * sizeof(uint32_t); + } + + m_RecValue = new uint8_t[m_RecValueLen]; + + size_t curOffset = 0; + m_RecValue[curOffset++] = 1; //pointer default value is 1 - means there are no empty timestamps + m_RecValue[curOffset++] = (uint8_t)timestampValue.type; // timestamp type + + int firstZero = -1; + for (int i = 0; i < (int)timestampValue.timestamps.size(); i++) + { + uint32_t timestamp = htobe32(timestampValue.timestamps.at(i)); + + // for pointer calculation - find the first timestamp equals to 0 + if (timestamp == 0 && firstZero == -1) + firstZero = i; + + if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP) + { + uint32_t ipAddrAsInt = timestampValue.ipAddresses.at(i).toInt(); + memcpy(m_RecValue + curOffset , &ipAddrAsInt, sizeof(uint32_t)); + curOffset += sizeof(uint32_t); + } + + memcpy(m_RecValue + curOffset , ×tamp, sizeof(uint32_t)); + curOffset += sizeof(uint32_t); + } + + // calculate pointer field + if (firstZero > -1) + { + uint8_t pointerVal = (uint8_t)(4 * sizeof(uint8_t) + firstZero * sizeof(uint32_t) + 1); + if (timestampValue.type == IPv4TimestampOptionValue::TimestampAndIP) + pointerVal += (uint8_t)(firstZero * sizeof(uint32_t)); + + m_RecValue[0] = pointerVal; + } + + m_BuilderParamsValid = true; +} + +IPv4Option IPv4OptionBuilder::build() const +{ + if (!m_BuilderParamsValid) + return IPv4Option(nullptr); + + size_t optionSize = m_RecValueLen + 2 * sizeof(uint8_t); + + uint8_t recType = static_cast(m_RecType); + if ((recType == (uint8_t)IPV4OPT_NOP || recType == (uint8_t)IPV4OPT_EndOfOptionsList)) + { + if (m_RecValueLen != 0) + { + PCPP_LOG_ERROR("Can't set IPv4 NOP option or IPv4 End-of-options option with size different than 0, tried to set size " << (int)m_RecValueLen); + return IPv4Option(nullptr); + } + + optionSize = sizeof(uint8_t); + } + + uint8_t* recordBuffer = new uint8_t[optionSize]; + memset(recordBuffer, 0, optionSize); + recordBuffer[0] = recType; + if (optionSize > 1) + { + recordBuffer[1] = static_cast(optionSize); + if (optionSize > 2 && m_RecValue != nullptr) + memcpy(recordBuffer + 2, m_RecValue, m_RecValueLen); + } + + return IPv4Option(recordBuffer); +} + + +/// ~~~~~~~~~ +/// IPv4Layer +/// ~~~~~~~~~ + + +void IPv4Layer::initLayer() +{ + const size_t headerLen = sizeof(iphdr); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + m_Protocol = IPv4; + memset(m_Data, 0, headerLen); + iphdr* ipHdr = getIPv4Header(); + ipHdr->internetHeaderLength = (5 & 0xf); + m_NumOfTrailingBytes = 0; + m_TempHeaderExtension = 0; +} + +void IPv4Layer::initLayerInPacket(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, bool setTotalLenAsDataLen) +{ + m_Protocol = IPv4; + m_NumOfTrailingBytes = 0; + m_TempHeaderExtension = 0; + if (setTotalLenAsDataLen) + { + size_t totalLen = be16toh(getIPv4Header()->totalLength); + // if totalLen == 0 this usually means TCP Segmentation Offload (TSO). In this case we should ignore the value of totalLen + // and look at the data captured on the wire + if ((totalLen < m_DataLen) && (totalLen !=0)) + m_DataLen = totalLen; + } +} + +void IPv4Layer::copyLayerData(const IPv4Layer& other) +{ + m_OptionReader = other.m_OptionReader; + m_NumOfTrailingBytes = other.m_NumOfTrailingBytes; + m_TempHeaderExtension = other.m_TempHeaderExtension; +} + +IPv4Layer::IPv4Layer() +{ + initLayer(); +} + +IPv4Layer::IPv4Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, bool setTotalLenAsDataLen) : Layer(data, dataLen, prevLayer, packet) +{ + initLayerInPacket(data, dataLen, prevLayer, packet, setTotalLenAsDataLen); +} + +IPv4Layer::IPv4Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) +{ + initLayerInPacket(data, dataLen, prevLayer, packet, true); +} + +IPv4Layer::IPv4Layer(const IPv4Address& srcIP, const IPv4Address& dstIP) +{ + initLayer(); + iphdr* ipHdr = getIPv4Header(); + ipHdr->ipSrc = srcIP.toInt(); + ipHdr->ipDst = dstIP.toInt(); +} + +IPv4Layer::IPv4Layer(const IPv4Layer& other) : Layer(other) +{ + copyLayerData(other); +} + +IPv4Layer& IPv4Layer::operator=(const IPv4Layer& other) +{ + Layer::operator=(other); + + copyLayerData(other); + + return *this; +} + +void IPv4Layer::parseNextLayer() +{ + size_t hdrLen = getHeaderLen(); + if (m_DataLen <= hdrLen || hdrLen == 0) + return; + + iphdr* ipHdr = getIPv4Header(); + + ProtocolType greVer = UnknownProtocol; + ProtocolType igmpVer = UnknownProtocol; + bool igmpQuery = false; + + uint8_t ipVersion = 0; + + uint8_t* payload = m_Data + hdrLen; + size_t payloadLen = m_DataLen - hdrLen; + + // If it's a fragment don't parse upper layers, unless if it's the first fragment + // TODO: assuming first fragment contains at least L4 header, what if it's not true? + if (isFragment()) + { + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + return; + } + + switch (ipHdr->protocol) + { + case PACKETPP_IPPROTO_UDP: + if (payloadLen >= sizeof(udphdr)) + m_NextLayer = new UdpLayer(payload, payloadLen, this, m_Packet); + break; + case PACKETPP_IPPROTO_TCP: + m_NextLayer = TcpLayer::isDataValid(payload, payloadLen) + ? static_cast(new TcpLayer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PACKETPP_IPPROTO_ICMP: + m_NextLayer = IcmpLayer::isDataValid(payload, payloadLen) + ? static_cast(new IcmpLayer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PACKETPP_IPPROTO_IPIP: + ipVersion = *payload >> 4; + if (ipVersion == 4 && IPv4Layer::isDataValid(payload, payloadLen)) + m_NextLayer = new IPv4Layer(payload, payloadLen, this, m_Packet); + else if (ipVersion == 6 && IPv6Layer::isDataValid(payload, payloadLen)) + m_NextLayer = new IPv6Layer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + case PACKETPP_IPPROTO_GRE: + greVer = GreLayer::getGREVersion(payload, payloadLen); + if (greVer == GREv0) + m_NextLayer = new GREv0Layer(payload, payloadLen, this, m_Packet); + else if (greVer == GREv1) + m_NextLayer = new GREv1Layer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + case PACKETPP_IPPROTO_IGMP: + igmpVer = IgmpLayer::getIGMPVerFromData( + payload, std::min(payloadLen, be16toh(getIPv4Header()->totalLength) - hdrLen), igmpQuery); + if (igmpVer == IGMPv1) + m_NextLayer = new IgmpV1Layer(payload, payloadLen, this, m_Packet); + else if (igmpVer == IGMPv2) + m_NextLayer = new IgmpV2Layer(payload, payloadLen, this, m_Packet); + else if (igmpVer == IGMPv3) + { + if (igmpQuery) + m_NextLayer = new IgmpV3QueryLayer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new IgmpV3ReportLayer(payload, payloadLen, this, m_Packet); + } + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + break; + case PACKETPP_IPPROTO_AH: + m_NextLayer = AuthenticationHeaderLayer::isDataValid(payload, payloadLen) + ? static_cast(new AuthenticationHeaderLayer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PACKETPP_IPPROTO_ESP: + m_NextLayer = ESPLayer::isDataValid(payload, payloadLen) + ? static_cast(new ESPLayer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PACKETPP_IPPROTO_IPV6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } +} + +void IPv4Layer::computeCalculateFields() +{ + iphdr* ipHdr = getIPv4Header(); + ipHdr->ipVersion = (4 & 0x0f); + ipHdr->totalLength = htobe16(m_DataLen); + ipHdr->headerChecksum = 0; + + if (m_NextLayer != nullptr) + { + switch (m_NextLayer->getProtocol()) + { + case TCP: + ipHdr->protocol = PACKETPP_IPPROTO_TCP; + break; + case UDP: + ipHdr->protocol = PACKETPP_IPPROTO_UDP; + break; + case ICMP: + ipHdr->protocol = PACKETPP_IPPROTO_ICMP; + break; + case GREv0: + case GREv1: + ipHdr->protocol = PACKETPP_IPPROTO_GRE; + break; + case IGMPv1: + case IGMPv2: + case IGMPv3: + ipHdr->protocol = PACKETPP_IPPROTO_IGMP; + break; + default: + break; + } + } + + ScalarBuffer scalar = { (uint16_t*)ipHdr, (size_t)(ipHdr->internetHeaderLength*4) } ; + ipHdr->headerChecksum = htobe16(computeChecksum(&scalar, 1)); +} + +bool IPv4Layer::isFragment() const +{ + return ((getFragmentFlags() & PCPP_IP_MORE_FRAGMENTS) != 0 || getFragmentOffset() != 0); +} + +bool IPv4Layer::isFirstFragment() const +{ + return isFragment() && (getFragmentOffset() == 0); +} + +bool IPv4Layer::isLastFragment() const +{ + return isFragment() && ((getFragmentFlags() & PCPP_IP_MORE_FRAGMENTS) == 0); +} + +uint8_t IPv4Layer::getFragmentFlags() const +{ + return getIPv4Header()->fragmentOffset & 0xE0; +} + +uint16_t IPv4Layer::getFragmentOffset() const +{ + return be16toh(getIPv4Header()->fragmentOffset & (uint16_t)0xFF1F) * 8; +} + +std::string IPv4Layer::toString() const +{ + std::string fragment = ""; + if (isFragment()) + { + if (isFirstFragment()) + fragment = "First fragment"; + else if (isLastFragment()) + fragment = "Last fragment"; + else + fragment = "Fragment"; + + std::stringstream sstm; + sstm << fragment << " [offset= " << getFragmentOffset() << "], "; + fragment = sstm.str(); + } + + return "IPv4 Layer, " + fragment + "Src: " + getSrcIPv4Address().toString() + ", Dst: " + getDstIPv4Address().toString(); +} + +IPv4Option IPv4Layer::getOption(IPv4OptionTypes option) const +{ + return m_OptionReader.getTLVRecord((uint8_t)option, getOptionsBasePtr(), getHeaderLen() - sizeof(iphdr)); +} + +IPv4Option IPv4Layer::getFirstOption() const +{ + return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), getHeaderLen() - sizeof(iphdr)); +} + +IPv4Option IPv4Layer::getNextOption(IPv4Option& option) const +{ + return m_OptionReader.getNextTLVRecord(option, getOptionsBasePtr(), getHeaderLen() - sizeof(iphdr)); +} + +size_t IPv4Layer::getOptionCount() const +{ + return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), getHeaderLen() - sizeof(iphdr)); +} + +void IPv4Layer::adjustOptionsTrailer(size_t totalOptSize) +{ + size_t ipHdrSize = sizeof(iphdr); + + int newNumberOfTrailingBytes = 0; + while ((totalOptSize + newNumberOfTrailingBytes) % 4 != 0) + newNumberOfTrailingBytes++; + + if (newNumberOfTrailingBytes < m_NumOfTrailingBytes) + shortenLayer(ipHdrSize+totalOptSize, m_NumOfTrailingBytes - newNumberOfTrailingBytes); + else if (newNumberOfTrailingBytes > m_NumOfTrailingBytes) + extendLayer(ipHdrSize+totalOptSize, newNumberOfTrailingBytes - m_NumOfTrailingBytes); + + m_NumOfTrailingBytes = newNumberOfTrailingBytes; + + for (int i = 0; i < m_NumOfTrailingBytes; i++) + m_Data[ipHdrSize + totalOptSize + i] = IPV4OPT_DUMMY; + + m_TempHeaderExtension = 0; + getIPv4Header()->internetHeaderLength = ((ipHdrSize + totalOptSize + m_NumOfTrailingBytes)/4 & 0x0f); +} + +IPv4Option IPv4Layer::addOptionAt(const IPv4OptionBuilder& optionBuilder, int offset) +{ + IPv4Option newOption = optionBuilder.build(); + if (newOption.isNull()) + return newOption; + + size_t sizeToExtend = newOption.getTotalSize(); + + size_t totalOptSize = getHeaderLen() - sizeof(iphdr) - m_NumOfTrailingBytes + sizeToExtend; + + if (totalOptSize > IPV4_MAX_OPT_SIZE) + { + PCPP_LOG_ERROR("Cannot add option - adding this option will exceed IPv4 total option size which is " << IPV4_MAX_OPT_SIZE); + newOption.purgeRecordData(); + return IPv4Option(nullptr); + } + + if (!extendLayer(offset, sizeToExtend)) + { + PCPP_LOG_ERROR("Could not extend IPv4Layer in [" << sizeToExtend << "] bytes"); + newOption.purgeRecordData(); + return IPv4Option(nullptr); + } + + memcpy(m_Data + offset, newOption.getRecordBasePtr(), newOption.getTotalSize()); + + newOption.purgeRecordData(); + + // setting this m_TempHeaderExtension because adjustOptionsTrailer() may extend or shorten the layer and the extend or shorten methods need to know the accurate + // current size of the header. m_TempHeaderExtension will be added to the length extracted from getIPv4Header()->internetHeaderLength as the temp new size + m_TempHeaderExtension = sizeToExtend; + adjustOptionsTrailer(totalOptSize); + // the adjustOptionsTrailer() adds or removed the trailing bytes and sets getIPv4Header()->internetHeaderLength to the correct size, so the m_TempHeaderExtension + // isn't needed anymore + m_TempHeaderExtension = 0; + + m_OptionReader.changeTLVRecordCount(1); + + uint8_t* newOptPtr = m_Data + offset; + + return IPv4Option(newOptPtr); +} + +IPv4Option IPv4Layer::addOption(const IPv4OptionBuilder& optionBuilder) +{ + return addOptionAt(optionBuilder, getHeaderLen() - m_NumOfTrailingBytes); +} + +IPv4Option IPv4Layer::addOptionAfter(const IPv4OptionBuilder& optionBuilder, IPv4OptionTypes prevOptionType) +{ + int offset = 0; + + IPv4Option prevOpt = getOption(prevOptionType); + + if (prevOpt.isNull()) + { + offset = sizeof(iphdr); + } + else + { + offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; + } + + return addOptionAt(optionBuilder, offset); +} + +bool IPv4Layer::removeOption(IPv4OptionTypes option) +{ + IPv4Option opt = getOption(option); + if (opt.isNull()) + { + return false; + } + + // calculate total option size + IPv4Option curOpt = getFirstOption(); + size_t totalOptSize = 0; + while (!curOpt.isNull()) + { + totalOptSize += curOpt.getTotalSize(); + curOpt = getNextOption(curOpt); + } + totalOptSize -= opt.getTotalSize(); + + + int offset = opt.getRecordBasePtr() - m_Data; + + size_t sizeToShorten = opt.getTotalSize(); + if (!shortenLayer(offset, sizeToShorten)) + { + PCPP_LOG_ERROR("Failed to remove IPv4 option: cannot shorten layer"); + return false; + } + + // setting this m_TempHeaderExtension because adjustOptionsTrailer() may extend or shorten the layer and the extend or shorten methods need to know the accurate + // current size of the header. m_TempHeaderExtension will be added to the length extracted from getIPv4Header()->internetHeaderLength as the temp new size + m_TempHeaderExtension = 0 - sizeToShorten; + adjustOptionsTrailer(totalOptSize); + // the adjustOptionsTrailer() adds or removed the trailing bytes and sets getIPv4Header()->internetHeaderLength to the correct size, so the m_TempHeaderExtension + // isn't needed anymore + m_TempHeaderExtension = 0; + + m_OptionReader.changeTLVRecordCount(-1); + + return true; +} + +bool IPv4Layer::removeAllOptions() +{ + int offset = sizeof(iphdr); + + if (!shortenLayer(offset, getHeaderLen() - offset)) + return false; + + getIPv4Header()->internetHeaderLength = (5 & 0xf); + m_NumOfTrailingBytes = 0; + m_OptionReader.changeTLVRecordCount(0 - getOptionCount()); + return true; +} + +} // namespace pcpp diff --git a/Packet++/src/IcmpLayer.cpp b/Packet++/src/IcmpLayer.cpp index 05a881f890..061c9ed8e2 100644 --- a/Packet++/src/IcmpLayer.cpp +++ b/Packet++/src/IcmpLayer.cpp @@ -1,699 +1,699 @@ -#define LOG_MODULE PacketLogModuleIcmpLayer - -#include "IcmpLayer.h" -#include "PayloadLayer.h" -#include "Packet.h" -#include "PacketUtils.h" -#include "Logger.h" -#include -#include -#include "EndianPortable.h" - -namespace pcpp -{ - -icmp_router_address_structure* icmp_router_advertisement::getRouterAddress(int index) const -{ - if (index < 0 || index >= header->advertisementCount) - return nullptr; - - uint8_t* headerAsByteArr = (uint8_t*)header; - return (icmp_router_address_structure*)(headerAsByteArr + sizeof(icmp_router_advertisement_hdr) + index * sizeof(icmp_router_address_structure)); -} - -void icmp_router_address_structure::setRouterAddress(IPv4Address addr, uint32_t preference) -{ - routerAddress = addr.toInt(); - preferenceLevel = htobe32(preference); -} - -IcmpLayer::IcmpLayer() : Layer() -{ - m_DataLen = sizeof(icmphdr); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = ICMP; -} - -IcmpMessageType IcmpLayer::getMessageType() const -{ - uint8_t type = getIcmpHeader()->type; - if (type > 18) - return ICMP_UNSUPPORTED; - - return (IcmpMessageType)type; -} - -bool IcmpLayer::cleanIcmpLayer() -{ - // remove all layers after - - if (m_Packet != nullptr) - { - bool res = m_Packet->removeAllLayersAfter(this); - if (!res) - return false; - } - - // shorten layer to size of icmphdr - - size_t headerLen = this->getHeaderLen(); - if (headerLen > sizeof(icmphdr)) - { - if (!this->shortenLayer(sizeof(icmphdr), headerLen - sizeof(icmphdr))) - return false; - } - - return true; -} - -bool IcmpLayer::setEchoData(IcmpMessageType echoType, uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, size_t dataLen) -{ - if (!cleanIcmpLayer()) - return false; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_echo_hdr) - sizeof(icmphdr) + dataLen)) - return false; - - getIcmpHeader()->type = (uint8_t)echoType; - - icmp_echo_request* header = nullptr; - if (echoType == ICMP_ECHO_REQUEST) - header = getEchoRequestData(); - else if (echoType == ICMP_ECHO_REPLY) - header = (icmp_echo_request*)getEchoReplyData(); - else - return false; - - header->header->code = 0; - header->header->checksum = 0; - header->header->id = htobe16(id); - header->header->sequence = htobe16(sequence); - header->header->timestamp = timestamp; - if (data != nullptr && dataLen > 0) - memcpy(header->data, data, dataLen); - - return true; -} - -bool IcmpLayer::setIpAndL4Layers(IPv4Layer* ipLayer, Layer* l4Layer) -{ - if (m_Packet == nullptr) - { - PCPP_LOG_ERROR("Cannot set ICMP data that involves IP and L4 layers on a layer not attached to a packet. " - "Please add the ICMP layer to a packet and try again"); - return false; - } - - if (ipLayer != nullptr && !m_Packet->addLayer(ipLayer)) - { - PCPP_LOG_ERROR("Couldn't add IP layer to ICMP packet"); - return false; - } - - if (l4Layer != nullptr && !m_Packet->addLayer(l4Layer)) - { - PCPP_LOG_ERROR("Couldn't add L4 layer to ICMP packet"); - return false; - } - - return true; -} - -icmp_echo_request* IcmpLayer::getEchoRequestData() -{ - if (!isMessageOfType(ICMP_ECHO_REQUEST)) - return nullptr; - - m_EchoData.header = (icmp_echo_hdr*)m_Data; - m_EchoData.data = (uint8_t*)(m_Data + sizeof(icmp_echo_hdr)); - m_EchoData.dataLength = m_DataLen - sizeof(icmp_echo_hdr); - - return &m_EchoData; -} - -icmp_echo_request* IcmpLayer::setEchoRequestData(uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, size_t dataLen) -{ - if (setEchoData(ICMP_ECHO_REQUEST, id, sequence, timestamp, data, dataLen)) - return getEchoRequestData(); - else - return nullptr; -} - -icmp_echo_reply* IcmpLayer::getEchoReplyData() -{ - if (!isMessageOfType(ICMP_ECHO_REPLY)) - return nullptr; - - m_EchoData.header = (icmp_echo_hdr*)m_Data; - m_EchoData.data = (uint8_t*)(m_Data + sizeof(icmp_echo_hdr)); - m_EchoData.dataLength = m_DataLen - sizeof(icmp_echo_hdr); - - return &m_EchoData; -} - -icmp_echo_reply* IcmpLayer::setEchoReplyData(uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, size_t dataLen) -{ - if (setEchoData(ICMP_ECHO_REPLY, id, sequence, timestamp, data, dataLen)) - return getEchoReplyData(); - else - return nullptr; -} - - -icmp_timestamp_request* IcmpLayer::getTimestampRequestData() -{ - if (!isMessageOfType(ICMP_TIMESTAMP_REQUEST)) - return nullptr; - - return (icmp_timestamp_request*)m_Data; -} - -icmp_timestamp_request* IcmpLayer::setTimestampRequestData(uint16_t id, uint16_t sequence, timeval originateTimestamp) -{ - if (!cleanIcmpLayer()) - return nullptr; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_timestamp_request) - sizeof(icmphdr))) - return nullptr; - - getIcmpHeader()->type = (uint8_t)ICMP_TIMESTAMP_REQUEST; - - icmp_timestamp_request* header = getTimestampRequestData(); - header->code = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - header->originateTimestamp = htobe32(originateTimestamp.tv_sec*1000 + originateTimestamp.tv_usec/1000); - header->receiveTimestamp = 0; - header->transmitTimestamp = 0; - - return header; -} - -icmp_timestamp_reply* IcmpLayer::getTimestampReplyData() -{ - if (!isMessageOfType(ICMP_TIMESTAMP_REPLY)) - return nullptr; - - return (icmp_timestamp_reply*)m_Data; -} - -icmp_timestamp_reply* IcmpLayer::setTimestampReplyData(uint16_t id, uint16_t sequence, - timeval originateTimestamp, timeval receiveTimestamp, timeval transmitTimestamp) -{ - if (!cleanIcmpLayer()) - return nullptr; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_timestamp_reply) - sizeof(icmphdr))) - return nullptr; - - getIcmpHeader()->type = (uint8_t)ICMP_TIMESTAMP_REPLY; - - icmp_timestamp_reply* header = getTimestampReplyData(); - header->code = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - header->originateTimestamp = htobe32(originateTimestamp.tv_sec*1000 + originateTimestamp.tv_usec/1000); - header->receiveTimestamp = htobe32(receiveTimestamp.tv_sec*1000 + receiveTimestamp.tv_usec/1000); - header->transmitTimestamp = htobe32(transmitTimestamp.tv_sec*1000 + transmitTimestamp.tv_usec/1000); - - return header; -} - -icmp_destination_unreachable* IcmpLayer::getDestUnreachableData() -{ - if (!isMessageOfType(ICMP_DEST_UNREACHABLE)) - return nullptr; - - return (icmp_destination_unreachable*)m_Data; -} - -icmp_destination_unreachable* IcmpLayer::setDestUnreachableData(IcmpDestUnreachableCodes code, uint16_t nextHopMTU, IPv4Layer* ipHeader, Layer* l4Header) -{ - if (!cleanIcmpLayer()) - return nullptr; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_destination_unreachable) - sizeof(icmphdr))) - return nullptr; - - getIcmpHeader()->type = (uint8_t)ICMP_DEST_UNREACHABLE; - - icmp_destination_unreachable* header = getDestUnreachableData(); - header->code = code; - header->nextHopMTU = htobe16(nextHopMTU); - header->unused = 0; - - if (!setIpAndL4Layers(ipHeader, l4Header)) - return nullptr; - - return header; -} - -icmp_source_quench* IcmpLayer::getSourceQuenchdata() -{ - if (!isMessageOfType(ICMP_SOURCE_QUENCH)) - return nullptr; - - return (icmp_source_quench*)m_Data; - -} - -icmp_source_quench* IcmpLayer::setSourceQuenchdata(IPv4Layer* ipHeader, Layer* l4Header) -{ - if (!cleanIcmpLayer()) - return nullptr; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_source_quench) - sizeof(icmphdr))) - return nullptr; - - getIcmpHeader()->type = (uint8_t)ICMP_SOURCE_QUENCH; - - icmp_source_quench* header = getSourceQuenchdata(); - header->unused = 0; - - if (!setIpAndL4Layers(ipHeader, l4Header)) - return nullptr; - - return header; -} - -icmp_redirect* IcmpLayer::getRedirectData() -{ - if (!isMessageOfType(ICMP_REDIRECT)) - return nullptr; - - return (icmp_redirect*)m_Data; -} - -icmp_redirect* IcmpLayer::setRedirectData(uint8_t code, IPv4Address gatewayAddress, IPv4Layer* ipHeader, Layer* l4Header) -{ - if (code > 3) - { - PCPP_LOG_ERROR("Unknown code " << (int)code << " for ICMP redirect data"); - return nullptr; - } - - if (!cleanIcmpLayer()) - return nullptr; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_redirect) - sizeof(icmphdr))) - return nullptr; - - getIcmpHeader()->type = (uint8_t)ICMP_REDIRECT; - - icmp_redirect* header = getRedirectData(); - header->code = code; - header->gatewayAddress = gatewayAddress.toInt(); - - if (!setIpAndL4Layers(ipHeader, l4Header)) - return nullptr; - - return header; -} - -icmp_router_advertisement* IcmpLayer::getRouterAdvertisementData() const -{ - if (!isMessageOfType(ICMP_ROUTER_ADV)) - return nullptr; - - m_RouterAdvData.header = (icmp_router_advertisement_hdr*)m_Data; - - return &m_RouterAdvData; -} - -icmp_router_advertisement* IcmpLayer::setRouterAdvertisementData(uint8_t code, uint16_t lifetimeInSeconds, const std::vector& routerAddresses) -{ - if (code != 0 && code != 16) - { - PCPP_LOG_ERROR("Unknown code " << (int)code << " for ICMP router advertisement data (only codes 0 and 16 are legal)"); - return nullptr; - } - - if (!cleanIcmpLayer()) - return nullptr; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_router_advertisement_hdr) + (routerAddresses.size()*sizeof(icmp_router_address_structure)) - sizeof(icmphdr))) - return nullptr; - - getIcmpHeader()->type = (uint8_t)ICMP_ROUTER_ADV; - - icmp_router_advertisement* header = getRouterAdvertisementData(); - header->header->code = code; - header->header->lifetime = htobe16(lifetimeInSeconds); - header->header->advertisementCount = (uint8_t)routerAddresses.size(); - header->header->addressEntrySize = 2; - - icmp_router_address_structure* curPos = (icmp_router_address_structure*)((uint8_t*)header->header + sizeof(icmp_router_advertisement_hdr)); - for (std::vector::const_iterator iter = routerAddresses.begin(); iter != routerAddresses.end(); iter++) - { - curPos->routerAddress = iter->routerAddress; - curPos->preferenceLevel = iter->preferenceLevel; - curPos += 1; - } - - return header; -} - -icmp_router_solicitation* IcmpLayer::getRouterSolicitationData() -{ - if (!isMessageOfType(ICMP_ROUTER_SOL)) - return nullptr; - - return (icmp_router_solicitation*)m_Data; -} - -icmp_router_solicitation* IcmpLayer::setRouterSolicitationData() -{ - if (!cleanIcmpLayer()) - return nullptr; - - getIcmpHeader()->type = (uint8_t)ICMP_ROUTER_SOL; - - icmp_router_solicitation* header = getRouterSolicitationData(); - header->code = 0; - - return header; -} - -icmp_time_exceeded* IcmpLayer::getTimeExceededData() -{ - if (!isMessageOfType(ICMP_TIME_EXCEEDED)) - return nullptr; - - return (icmp_time_exceeded*)m_Data; -} - -icmp_time_exceeded* IcmpLayer::setTimeExceededData(uint8_t code, IPv4Layer* ipHeader, Layer* l4Header) -{ - if (code > 1) - { - PCPP_LOG_ERROR("Unknown code " << (int)code << " for ICMP time exceeded data"); - return nullptr; - } - - if (!cleanIcmpLayer()) - return nullptr; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_time_exceeded) - sizeof(icmphdr))) - return nullptr; - - getIcmpHeader()->type = (uint8_t)ICMP_TIME_EXCEEDED; - - icmp_time_exceeded* header = getTimeExceededData(); - header->code = code; - header->unused = 0; - - if (!setIpAndL4Layers(ipHeader, l4Header)) - return nullptr; - - return header; -} - -icmp_param_problem* IcmpLayer::getParamProblemData() -{ - if (!isMessageOfType(ICMP_PARAM_PROBLEM)) - return nullptr; - - return (icmp_param_problem*)m_Data; -} - -icmp_param_problem* IcmpLayer::setParamProblemData(uint8_t code, uint8_t errorOctetPointer, IPv4Layer* ipHeader, Layer* l4Header) -{ - if (code > 2) - { - PCPP_LOG_ERROR("Unknown code " << (int)code << " for ICMP parameter problem data"); - return nullptr; - } - - if (!cleanIcmpLayer()) - return nullptr; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_param_problem) - sizeof(icmphdr))) - return nullptr; - - getIcmpHeader()->type = (uint8_t)ICMP_PARAM_PROBLEM; - - icmp_param_problem* header = getParamProblemData(); - header->code = code; - header->unused1 = 0; - header->unused2 = 0; - header->pointer = errorOctetPointer; - - if (!setIpAndL4Layers(ipHeader, l4Header)) - return nullptr; - - return header; -} - -icmp_address_mask_request* IcmpLayer::getAddressMaskRequestData() -{ - if (!isMessageOfType(ICMP_ADDRESS_MASK_REQUEST)) - return nullptr; - - return (icmp_address_mask_request*)m_Data; -} - -icmp_address_mask_request* IcmpLayer::setAddressMaskRequestData(uint16_t id, uint16_t sequence, IPv4Address mask) -{ - if (!cleanIcmpLayer()) - return nullptr; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_address_mask_request) - sizeof(icmphdr))) - return nullptr; - - getIcmpHeader()->type = (uint8_t)ICMP_ADDRESS_MASK_REQUEST; - - icmp_address_mask_request* header = getAddressMaskRequestData(); - header->code = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - header->addressMask = mask.toInt(); - - return header; -} - -icmp_address_mask_reply* IcmpLayer::getAddressMaskReplyData() -{ - if (!isMessageOfType(ICMP_ADDRESS_MASK_REPLY)) - return nullptr; - - return (icmp_address_mask_reply*)m_Data; -} - -icmp_address_mask_reply* IcmpLayer::setAddressMaskReplyData(uint16_t id, uint16_t sequence, IPv4Address mask) -{ - if (!cleanIcmpLayer()) - return nullptr; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_address_mask_reply) - sizeof(icmphdr))) - return nullptr; - - getIcmpHeader()->type = (uint8_t)ICMP_ADDRESS_MASK_REPLY; - - icmp_address_mask_reply* header = getAddressMaskReplyData(); - header->code = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - header->addressMask = htobe32(mask.toInt()); - - return header; -} - -icmp_info_request* IcmpLayer::getInfoRequestData() -{ - if (!isMessageOfType(ICMP_INFO_REQUEST)) - return nullptr; - - return (icmp_info_request*)m_Data; -} - -icmp_info_request* IcmpLayer::setInfoRequestData(uint16_t id, uint16_t sequence) -{ - if (!cleanIcmpLayer()) - return nullptr; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_info_request) - sizeof(icmphdr))) - return nullptr; - - getIcmpHeader()->type = (uint8_t)ICMP_INFO_REQUEST; - - icmp_info_request* header = getInfoRequestData(); - header->code = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - - return header; -} - -icmp_info_reply* IcmpLayer::getInfoReplyData() -{ - if (!isMessageOfType(ICMP_INFO_REPLY)) - return nullptr; - - return (icmp_info_reply*)m_Data; -} - -icmp_info_reply* IcmpLayer::setInfoReplyData(uint16_t id, uint16_t sequence) -{ - if (!cleanIcmpLayer()) - return nullptr; - - if (!this->extendLayer(m_DataLen, sizeof(icmp_info_reply) - sizeof(icmphdr))) - return nullptr; - - getIcmpHeader()->type = (uint8_t)ICMP_INFO_REPLY; - - icmp_info_reply* header = getInfoReplyData(); - header->code = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - - return header; -} - - -void IcmpLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - - switch (getMessageType()) - { - case ICMP_DEST_UNREACHABLE: - case ICMP_SOURCE_QUENCH: - case ICMP_TIME_EXCEEDED: - case ICMP_REDIRECT: - case ICMP_PARAM_PROBLEM: - m_NextLayer = IPv4Layer::isDataValid(m_Data + headerLen, m_DataLen - headerLen) - ? static_cast(new IPv4Layer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet)) - : static_cast(new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet)); - return; - default: - if (m_DataLen > headerLen) - m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); - return; - } -} - -size_t IcmpLayer::getHeaderLen() const -{ - IcmpMessageType type = getMessageType(); - size_t routerAdvSize = 0; - switch (type) - { - case ICMP_ECHO_REQUEST: - case ICMP_ECHO_REPLY: - return m_DataLen; - case ICMP_TIMESTAMP_REQUEST: - case ICMP_TIMESTAMP_REPLY: - return sizeof(icmp_timestamp_request); - case ICMP_ROUTER_SOL: - case ICMP_INFO_REQUEST: - case ICMP_INFO_REPLY: - case ICMP_UNSUPPORTED: - return sizeof(icmphdr); - case ICMP_ADDRESS_MASK_REPLY: - case ICMP_ADDRESS_MASK_REQUEST: - return sizeof(icmp_address_mask_request); - case ICMP_DEST_UNREACHABLE: - return sizeof(icmp_destination_unreachable); - case ICMP_REDIRECT: - return sizeof(icmp_redirect); - case ICMP_TIME_EXCEEDED: - case ICMP_SOURCE_QUENCH: - return sizeof(icmp_time_exceeded); - case ICMP_PARAM_PROBLEM: - return sizeof(icmp_param_problem); - case ICMP_ROUTER_ADV: - routerAdvSize = sizeof(icmp_router_advertisement_hdr) + (getRouterAdvertisementData()->header->advertisementCount*sizeof(icmp_router_address_structure)); - if (routerAdvSize > m_DataLen) - return m_DataLen; - return routerAdvSize; - default: - return sizeof(icmphdr); - } -} - -void IcmpLayer::computeCalculateFields() -{ - // calculate checksum - getIcmpHeader()->checksum = 0; - - size_t icmpLen = 0; - Layer* curLayer = this; - while (curLayer != nullptr) - { - icmpLen += curLayer->getHeaderLen(); - curLayer = curLayer->getNextLayer(); - } - - ScalarBuffer buffer; - buffer.buffer = (uint16_t*)getIcmpHeader(); - buffer.len = icmpLen; - size_t checksum = computeChecksum(&buffer, 1); - - getIcmpHeader()->checksum = htobe16(checksum); -} - -std::string IcmpLayer::toString() const -{ - std::string messageTypeAsString; - IcmpMessageType type = getMessageType(); - switch (type) - { - case ICMP_ECHO_REPLY: - messageTypeAsString = "Echo (ping) reply"; - break; - case ICMP_DEST_UNREACHABLE: - messageTypeAsString = "Destination unreachable"; - break; - case ICMP_SOURCE_QUENCH: - messageTypeAsString = "Source quench (flow control)"; - break; - case ICMP_REDIRECT: - messageTypeAsString = "Redirect"; - break; - case ICMP_ECHO_REQUEST: - messageTypeAsString = "Echo (ping) request"; - break; - case ICMP_ROUTER_ADV: - messageTypeAsString = "Router advertisement"; - break; - case ICMP_ROUTER_SOL: - messageTypeAsString = "Router solicitation"; - break; - case ICMP_TIME_EXCEEDED: - messageTypeAsString = "Time-to-live exceeded"; - break; - case ICMP_PARAM_PROBLEM: - messageTypeAsString = "Parameter problem: bad IP header"; - break; - case ICMP_TIMESTAMP_REQUEST: - messageTypeAsString = "Timestamp request"; - break; - case ICMP_TIMESTAMP_REPLY: - messageTypeAsString = "Timestamp reply"; - break; - case ICMP_INFO_REQUEST: - messageTypeAsString = "Information request"; - break; - case ICMP_INFO_REPLY: - messageTypeAsString = "Information reply"; - break; - case ICMP_ADDRESS_MASK_REQUEST: - messageTypeAsString = "Address mask request"; - break; - case ICMP_ADDRESS_MASK_REPLY: - messageTypeAsString = "Address mask reply"; - break; - default: - messageTypeAsString = "Unknown"; - break; - } - - std::ostringstream typeStream; - typeStream << (int)getIcmpHeader()->type; - - return "ICMP Layer, " + messageTypeAsString + " (type: " + typeStream.str() + ")"; -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModuleIcmpLayer + +#include "IcmpLayer.h" +#include "PayloadLayer.h" +#include "Packet.h" +#include "PacketUtils.h" +#include "Logger.h" +#include +#include +#include "EndianPortable.h" + +namespace pcpp +{ + +icmp_router_address_structure* icmp_router_advertisement::getRouterAddress(int index) const +{ + if (index < 0 || index >= header->advertisementCount) + return nullptr; + + uint8_t* headerAsByteArr = (uint8_t*)header; + return (icmp_router_address_structure*)(headerAsByteArr + sizeof(icmp_router_advertisement_hdr) + index * sizeof(icmp_router_address_structure)); +} + +void icmp_router_address_structure::setRouterAddress(IPv4Address addr, uint32_t preference) +{ + routerAddress = addr.toInt(); + preferenceLevel = htobe32(preference); +} + +IcmpLayer::IcmpLayer() : Layer() +{ + m_DataLen = sizeof(icmphdr); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = ICMP; +} + +IcmpMessageType IcmpLayer::getMessageType() const +{ + uint8_t type = getIcmpHeader()->type; + if (type > 18) + return ICMP_UNSUPPORTED; + + return (IcmpMessageType)type; +} + +bool IcmpLayer::cleanIcmpLayer() +{ + // remove all layers after + + if (m_Packet != nullptr) + { + bool res = m_Packet->removeAllLayersAfter(this); + if (!res) + return false; + } + + // shorten layer to size of icmphdr + + size_t headerLen = this->getHeaderLen(); + if (headerLen > sizeof(icmphdr)) + { + if (!this->shortenLayer(sizeof(icmphdr), headerLen - sizeof(icmphdr))) + return false; + } + + return true; +} + +bool IcmpLayer::setEchoData(IcmpMessageType echoType, uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, size_t dataLen) +{ + if (!cleanIcmpLayer()) + return false; + + if (!this->extendLayer(m_DataLen, sizeof(icmp_echo_hdr) - sizeof(icmphdr) + dataLen)) + return false; + + getIcmpHeader()->type = (uint8_t)echoType; + + icmp_echo_request* header = nullptr; + if (echoType == ICMP_ECHO_REQUEST) + header = getEchoRequestData(); + else if (echoType == ICMP_ECHO_REPLY) + header = (icmp_echo_request*)getEchoReplyData(); + else + return false; + + header->header->code = 0; + header->header->checksum = 0; + header->header->id = htobe16(id); + header->header->sequence = htobe16(sequence); + header->header->timestamp = timestamp; + if (data != nullptr && dataLen > 0) + memcpy(header->data, data, dataLen); + + return true; +} + +bool IcmpLayer::setIpAndL4Layers(IPv4Layer* ipLayer, Layer* l4Layer) +{ + if (m_Packet == nullptr) + { + PCPP_LOG_ERROR("Cannot set ICMP data that involves IP and L4 layers on a layer not attached to a packet. " + "Please add the ICMP layer to a packet and try again"); + return false; + } + + if (ipLayer != nullptr && !m_Packet->addLayer(ipLayer)) + { + PCPP_LOG_ERROR("Couldn't add IP layer to ICMP packet"); + return false; + } + + if (l4Layer != nullptr && !m_Packet->addLayer(l4Layer)) + { + PCPP_LOG_ERROR("Couldn't add L4 layer to ICMP packet"); + return false; + } + + return true; +} + +icmp_echo_request* IcmpLayer::getEchoRequestData() +{ + if (!isMessageOfType(ICMP_ECHO_REQUEST)) + return nullptr; + + m_EchoData.header = (icmp_echo_hdr*)m_Data; + m_EchoData.data = (uint8_t*)(m_Data + sizeof(icmp_echo_hdr)); + m_EchoData.dataLength = m_DataLen - sizeof(icmp_echo_hdr); + + return &m_EchoData; +} + +icmp_echo_request* IcmpLayer::setEchoRequestData(uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, size_t dataLen) +{ + if (setEchoData(ICMP_ECHO_REQUEST, id, sequence, timestamp, data, dataLen)) + return getEchoRequestData(); + else + return nullptr; +} + +icmp_echo_reply* IcmpLayer::getEchoReplyData() +{ + if (!isMessageOfType(ICMP_ECHO_REPLY)) + return nullptr; + + m_EchoData.header = (icmp_echo_hdr*)m_Data; + m_EchoData.data = (uint8_t*)(m_Data + sizeof(icmp_echo_hdr)); + m_EchoData.dataLength = m_DataLen - sizeof(icmp_echo_hdr); + + return &m_EchoData; +} + +icmp_echo_reply* IcmpLayer::setEchoReplyData(uint16_t id, uint16_t sequence, uint64_t timestamp, const uint8_t* data, size_t dataLen) +{ + if (setEchoData(ICMP_ECHO_REPLY, id, sequence, timestamp, data, dataLen)) + return getEchoReplyData(); + else + return nullptr; +} + + +icmp_timestamp_request* IcmpLayer::getTimestampRequestData() +{ + if (!isMessageOfType(ICMP_TIMESTAMP_REQUEST)) + return nullptr; + + return (icmp_timestamp_request*)m_Data; +} + +icmp_timestamp_request* IcmpLayer::setTimestampRequestData(uint16_t id, uint16_t sequence, timeval originateTimestamp) +{ + if (!cleanIcmpLayer()) + return nullptr; + + if (!this->extendLayer(m_DataLen, sizeof(icmp_timestamp_request) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_TIMESTAMP_REQUEST; + + icmp_timestamp_request* header = getTimestampRequestData(); + header->code = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); + header->originateTimestamp = htobe32(originateTimestamp.tv_sec*1000 + originateTimestamp.tv_usec/1000); + header->receiveTimestamp = 0; + header->transmitTimestamp = 0; + + return header; +} + +icmp_timestamp_reply* IcmpLayer::getTimestampReplyData() +{ + if (!isMessageOfType(ICMP_TIMESTAMP_REPLY)) + return nullptr; + + return (icmp_timestamp_reply*)m_Data; +} + +icmp_timestamp_reply* IcmpLayer::setTimestampReplyData(uint16_t id, uint16_t sequence, + timeval originateTimestamp, timeval receiveTimestamp, timeval transmitTimestamp) +{ + if (!cleanIcmpLayer()) + return nullptr; + + if (!this->extendLayer(m_DataLen, sizeof(icmp_timestamp_reply) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_TIMESTAMP_REPLY; + + icmp_timestamp_reply* header = getTimestampReplyData(); + header->code = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); + header->originateTimestamp = htobe32(originateTimestamp.tv_sec*1000 + originateTimestamp.tv_usec/1000); + header->receiveTimestamp = htobe32(receiveTimestamp.tv_sec*1000 + receiveTimestamp.tv_usec/1000); + header->transmitTimestamp = htobe32(transmitTimestamp.tv_sec*1000 + transmitTimestamp.tv_usec/1000); + + return header; +} + +icmp_destination_unreachable* IcmpLayer::getDestUnreachableData() +{ + if (!isMessageOfType(ICMP_DEST_UNREACHABLE)) + return nullptr; + + return (icmp_destination_unreachable*)m_Data; +} + +icmp_destination_unreachable* IcmpLayer::setDestUnreachableData(IcmpDestUnreachableCodes code, uint16_t nextHopMTU, IPv4Layer* ipHeader, Layer* l4Header) +{ + if (!cleanIcmpLayer()) + return nullptr; + + if (!this->extendLayer(m_DataLen, sizeof(icmp_destination_unreachable) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_DEST_UNREACHABLE; + + icmp_destination_unreachable* header = getDestUnreachableData(); + header->code = code; + header->nextHopMTU = htobe16(nextHopMTU); + header->unused = 0; + + if (!setIpAndL4Layers(ipHeader, l4Header)) + return nullptr; + + return header; +} + +icmp_source_quench* IcmpLayer::getSourceQuenchdata() +{ + if (!isMessageOfType(ICMP_SOURCE_QUENCH)) + return nullptr; + + return (icmp_source_quench*)m_Data; + +} + +icmp_source_quench* IcmpLayer::setSourceQuenchdata(IPv4Layer* ipHeader, Layer* l4Header) +{ + if (!cleanIcmpLayer()) + return nullptr; + + if (!this->extendLayer(m_DataLen, sizeof(icmp_source_quench) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_SOURCE_QUENCH; + + icmp_source_quench* header = getSourceQuenchdata(); + header->unused = 0; + + if (!setIpAndL4Layers(ipHeader, l4Header)) + return nullptr; + + return header; +} + +icmp_redirect* IcmpLayer::getRedirectData() +{ + if (!isMessageOfType(ICMP_REDIRECT)) + return nullptr; + + return (icmp_redirect*)m_Data; +} + +icmp_redirect* IcmpLayer::setRedirectData(uint8_t code, IPv4Address gatewayAddress, IPv4Layer* ipHeader, Layer* l4Header) +{ + if (code > 3) + { + PCPP_LOG_ERROR("Unknown code " << (int)code << " for ICMP redirect data"); + return nullptr; + } + + if (!cleanIcmpLayer()) + return nullptr; + + if (!this->extendLayer(m_DataLen, sizeof(icmp_redirect) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_REDIRECT; + + icmp_redirect* header = getRedirectData(); + header->code = code; + header->gatewayAddress = gatewayAddress.toInt(); + + if (!setIpAndL4Layers(ipHeader, l4Header)) + return nullptr; + + return header; +} + +icmp_router_advertisement* IcmpLayer::getRouterAdvertisementData() const +{ + if (!isMessageOfType(ICMP_ROUTER_ADV)) + return nullptr; + + m_RouterAdvData.header = (icmp_router_advertisement_hdr*)m_Data; + + return &m_RouterAdvData; +} + +icmp_router_advertisement* IcmpLayer::setRouterAdvertisementData(uint8_t code, uint16_t lifetimeInSeconds, const std::vector& routerAddresses) +{ + if (code != 0 && code != 16) + { + PCPP_LOG_ERROR("Unknown code " << (int)code << " for ICMP router advertisement data (only codes 0 and 16 are legal)"); + return nullptr; + } + + if (!cleanIcmpLayer()) + return nullptr; + + if (!this->extendLayer(m_DataLen, sizeof(icmp_router_advertisement_hdr) + (routerAddresses.size()*sizeof(icmp_router_address_structure)) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_ROUTER_ADV; + + icmp_router_advertisement* header = getRouterAdvertisementData(); + header->header->code = code; + header->header->lifetime = htobe16(lifetimeInSeconds); + header->header->advertisementCount = (uint8_t)routerAddresses.size(); + header->header->addressEntrySize = 2; + + icmp_router_address_structure* curPos = (icmp_router_address_structure*)((uint8_t*)header->header + sizeof(icmp_router_advertisement_hdr)); + for (std::vector::const_iterator iter = routerAddresses.begin(); iter != routerAddresses.end(); iter++) + { + curPos->routerAddress = iter->routerAddress; + curPos->preferenceLevel = iter->preferenceLevel; + curPos += 1; + } + + return header; +} + +icmp_router_solicitation* IcmpLayer::getRouterSolicitationData() +{ + if (!isMessageOfType(ICMP_ROUTER_SOL)) + return nullptr; + + return (icmp_router_solicitation*)m_Data; +} + +icmp_router_solicitation* IcmpLayer::setRouterSolicitationData() +{ + if (!cleanIcmpLayer()) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_ROUTER_SOL; + + icmp_router_solicitation* header = getRouterSolicitationData(); + header->code = 0; + + return header; +} + +icmp_time_exceeded* IcmpLayer::getTimeExceededData() +{ + if (!isMessageOfType(ICMP_TIME_EXCEEDED)) + return nullptr; + + return (icmp_time_exceeded*)m_Data; +} + +icmp_time_exceeded* IcmpLayer::setTimeExceededData(uint8_t code, IPv4Layer* ipHeader, Layer* l4Header) +{ + if (code > 1) + { + PCPP_LOG_ERROR("Unknown code " << (int)code << " for ICMP time exceeded data"); + return nullptr; + } + + if (!cleanIcmpLayer()) + return nullptr; + + if (!this->extendLayer(m_DataLen, sizeof(icmp_time_exceeded) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_TIME_EXCEEDED; + + icmp_time_exceeded* header = getTimeExceededData(); + header->code = code; + header->unused = 0; + + if (!setIpAndL4Layers(ipHeader, l4Header)) + return nullptr; + + return header; +} + +icmp_param_problem* IcmpLayer::getParamProblemData() +{ + if (!isMessageOfType(ICMP_PARAM_PROBLEM)) + return nullptr; + + return (icmp_param_problem*)m_Data; +} + +icmp_param_problem* IcmpLayer::setParamProblemData(uint8_t code, uint8_t errorOctetPointer, IPv4Layer* ipHeader, Layer* l4Header) +{ + if (code > 2) + { + PCPP_LOG_ERROR("Unknown code " << (int)code << " for ICMP parameter problem data"); + return nullptr; + } + + if (!cleanIcmpLayer()) + return nullptr; + + if (!this->extendLayer(m_DataLen, sizeof(icmp_param_problem) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_PARAM_PROBLEM; + + icmp_param_problem* header = getParamProblemData(); + header->code = code; + header->unused1 = 0; + header->unused2 = 0; + header->pointer = errorOctetPointer; + + if (!setIpAndL4Layers(ipHeader, l4Header)) + return nullptr; + + return header; +} + +icmp_address_mask_request* IcmpLayer::getAddressMaskRequestData() +{ + if (!isMessageOfType(ICMP_ADDRESS_MASK_REQUEST)) + return nullptr; + + return (icmp_address_mask_request*)m_Data; +} + +icmp_address_mask_request* IcmpLayer::setAddressMaskRequestData(uint16_t id, uint16_t sequence, IPv4Address mask) +{ + if (!cleanIcmpLayer()) + return nullptr; + + if (!this->extendLayer(m_DataLen, sizeof(icmp_address_mask_request) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_ADDRESS_MASK_REQUEST; + + icmp_address_mask_request* header = getAddressMaskRequestData(); + header->code = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); + header->addressMask = mask.toInt(); + + return header; +} + +icmp_address_mask_reply* IcmpLayer::getAddressMaskReplyData() +{ + if (!isMessageOfType(ICMP_ADDRESS_MASK_REPLY)) + return nullptr; + + return (icmp_address_mask_reply*)m_Data; +} + +icmp_address_mask_reply* IcmpLayer::setAddressMaskReplyData(uint16_t id, uint16_t sequence, IPv4Address mask) +{ + if (!cleanIcmpLayer()) + return nullptr; + + if (!this->extendLayer(m_DataLen, sizeof(icmp_address_mask_reply) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_ADDRESS_MASK_REPLY; + + icmp_address_mask_reply* header = getAddressMaskReplyData(); + header->code = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); + header->addressMask = htobe32(mask.toInt()); + + return header; +} + +icmp_info_request* IcmpLayer::getInfoRequestData() +{ + if (!isMessageOfType(ICMP_INFO_REQUEST)) + return nullptr; + + return (icmp_info_request*)m_Data; +} + +icmp_info_request* IcmpLayer::setInfoRequestData(uint16_t id, uint16_t sequence) +{ + if (!cleanIcmpLayer()) + return nullptr; + + if (!this->extendLayer(m_DataLen, sizeof(icmp_info_request) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_INFO_REQUEST; + + icmp_info_request* header = getInfoRequestData(); + header->code = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); + + return header; +} + +icmp_info_reply* IcmpLayer::getInfoReplyData() +{ + if (!isMessageOfType(ICMP_INFO_REPLY)) + return nullptr; + + return (icmp_info_reply*)m_Data; +} + +icmp_info_reply* IcmpLayer::setInfoReplyData(uint16_t id, uint16_t sequence) +{ + if (!cleanIcmpLayer()) + return nullptr; + + if (!this->extendLayer(m_DataLen, sizeof(icmp_info_reply) - sizeof(icmphdr))) + return nullptr; + + getIcmpHeader()->type = (uint8_t)ICMP_INFO_REPLY; + + icmp_info_reply* header = getInfoReplyData(); + header->code = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); + + return header; +} + + +void IcmpLayer::parseNextLayer() +{ + size_t headerLen = getHeaderLen(); + + switch (getMessageType()) + { + case ICMP_DEST_UNREACHABLE: + case ICMP_SOURCE_QUENCH: + case ICMP_TIME_EXCEEDED: + case ICMP_REDIRECT: + case ICMP_PARAM_PROBLEM: + m_NextLayer = IPv4Layer::isDataValid(m_Data + headerLen, m_DataLen - headerLen) + ? static_cast(new IPv4Layer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet)) + : static_cast(new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet)); + return; + default: + if (m_DataLen > headerLen) + m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); + return; + } +} + +size_t IcmpLayer::getHeaderLen() const +{ + IcmpMessageType type = getMessageType(); + size_t routerAdvSize = 0; + switch (type) + { + case ICMP_ECHO_REQUEST: + case ICMP_ECHO_REPLY: + return m_DataLen; + case ICMP_TIMESTAMP_REQUEST: + case ICMP_TIMESTAMP_REPLY: + return sizeof(icmp_timestamp_request); + case ICMP_ROUTER_SOL: + case ICMP_INFO_REQUEST: + case ICMP_INFO_REPLY: + case ICMP_UNSUPPORTED: + return sizeof(icmphdr); + case ICMP_ADDRESS_MASK_REPLY: + case ICMP_ADDRESS_MASK_REQUEST: + return sizeof(icmp_address_mask_request); + case ICMP_DEST_UNREACHABLE: + return sizeof(icmp_destination_unreachable); + case ICMP_REDIRECT: + return sizeof(icmp_redirect); + case ICMP_TIME_EXCEEDED: + case ICMP_SOURCE_QUENCH: + return sizeof(icmp_time_exceeded); + case ICMP_PARAM_PROBLEM: + return sizeof(icmp_param_problem); + case ICMP_ROUTER_ADV: + routerAdvSize = sizeof(icmp_router_advertisement_hdr) + (getRouterAdvertisementData()->header->advertisementCount*sizeof(icmp_router_address_structure)); + if (routerAdvSize > m_DataLen) + return m_DataLen; + return routerAdvSize; + default: + return sizeof(icmphdr); + } +} + +void IcmpLayer::computeCalculateFields() +{ + // calculate checksum + getIcmpHeader()->checksum = 0; + + size_t icmpLen = 0; + Layer* curLayer = this; + while (curLayer != nullptr) + { + icmpLen += curLayer->getHeaderLen(); + curLayer = curLayer->getNextLayer(); + } + + ScalarBuffer buffer; + buffer.buffer = (uint16_t*)getIcmpHeader(); + buffer.len = icmpLen; + size_t checksum = computeChecksum(&buffer, 1); + + getIcmpHeader()->checksum = htobe16(checksum); +} + +std::string IcmpLayer::toString() const +{ + std::string messageTypeAsString; + IcmpMessageType type = getMessageType(); + switch (type) + { + case ICMP_ECHO_REPLY: + messageTypeAsString = "Echo (ping) reply"; + break; + case ICMP_DEST_UNREACHABLE: + messageTypeAsString = "Destination unreachable"; + break; + case ICMP_SOURCE_QUENCH: + messageTypeAsString = "Source quench (flow control)"; + break; + case ICMP_REDIRECT: + messageTypeAsString = "Redirect"; + break; + case ICMP_ECHO_REQUEST: + messageTypeAsString = "Echo (ping) request"; + break; + case ICMP_ROUTER_ADV: + messageTypeAsString = "Router advertisement"; + break; + case ICMP_ROUTER_SOL: + messageTypeAsString = "Router solicitation"; + break; + case ICMP_TIME_EXCEEDED: + messageTypeAsString = "Time-to-live exceeded"; + break; + case ICMP_PARAM_PROBLEM: + messageTypeAsString = "Parameter problem: bad IP header"; + break; + case ICMP_TIMESTAMP_REQUEST: + messageTypeAsString = "Timestamp request"; + break; + case ICMP_TIMESTAMP_REPLY: + messageTypeAsString = "Timestamp reply"; + break; + case ICMP_INFO_REQUEST: + messageTypeAsString = "Information request"; + break; + case ICMP_INFO_REPLY: + messageTypeAsString = "Information reply"; + break; + case ICMP_ADDRESS_MASK_REQUEST: + messageTypeAsString = "Address mask request"; + break; + case ICMP_ADDRESS_MASK_REPLY: + messageTypeAsString = "Address mask reply"; + break; + default: + messageTypeAsString = "Unknown"; + break; + } + + std::ostringstream typeStream; + typeStream << (int)getIcmpHeader()->type; + + return "ICMP Layer, " + messageTypeAsString + " (type: " + typeStream.str() + ")"; +} + +} // namespace pcpp diff --git a/Packet++/src/IcmpV6Layer.cpp b/Packet++/src/IcmpV6Layer.cpp index f6be447556..7fab0db9da 100644 --- a/Packet++/src/IcmpV6Layer.cpp +++ b/Packet++/src/IcmpV6Layer.cpp @@ -1,169 +1,169 @@ -#define LOG_MODULE PacketLogModuleIcmpV6Layer - -#include "IcmpV6Layer.h" -#include "EndianPortable.h" -#include "IPv4Layer.h" -#include "IPv6Layer.h" -#include "NdpLayer.h" -#include "PacketUtils.h" -#include "PayloadLayer.h" -#include -#include - -// IcmpV6Layer - -namespace pcpp -{ - -Layer* IcmpV6Layer::parseIcmpV6Layer(uint8_t *data, size_t dataLen, Layer* prevLayer, Packet* packet) -{ - if (dataLen < sizeof(icmpv6hdr)) - return new PayloadLayer(data, dataLen, prevLayer, packet); - - icmpv6hdr *hdr = (icmpv6hdr *)data; - ICMPv6MessageType messageType = static_cast(hdr->type); - - switch (messageType) - { - case ICMPv6MessageType::ICMPv6_ECHO_REQUEST: - case ICMPv6MessageType::ICMPv6_ECHO_REPLY: - return new ICMPv6EchoLayer(data, dataLen, prevLayer, packet); - case ICMPv6MessageType::ICMPv6_NEIGHBOR_SOLICITATION: - return new NDPNeighborSolicitationLayer(data, dataLen, prevLayer, packet); - case ICMPv6MessageType::ICMPv6_NEIGHBOR_ADVERTISEMENT: - return new NDPNeighborAdvertisementLayer(data, dataLen, prevLayer, packet); - case ICMPv6MessageType::ICMPv6_UNKNOWN_MESSAGE: - return new PayloadLayer(data, dataLen, prevLayer, packet); - default: - return new IcmpV6Layer(data, dataLen, prevLayer, packet); - } -} - -IcmpV6Layer::IcmpV6Layer(ICMPv6MessageType msgType, uint8_t code, const uint8_t *data, size_t dataLen) -{ - m_DataLen = sizeof(icmpv6hdr) + dataLen; - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = ICMPv6; - - icmpv6hdr *hdr = (icmpv6hdr *)m_Data; - hdr->type = static_cast(msgType); - hdr->code = code; - - if (data != nullptr && dataLen > 0) - memcpy(m_Data + sizeof(icmpv6hdr), data, dataLen); -} - -ICMPv6MessageType IcmpV6Layer::getMessageType() const -{ - return static_cast(getIcmpv6Header()->type); -} - -uint8_t IcmpV6Layer::getCode() const -{ - return getIcmpv6Header()->code; -} - -uint16_t IcmpV6Layer::getChecksum() const -{ - return be16toh(getIcmpv6Header()->checksum); -} - -void IcmpV6Layer::computeCalculateFields() -{ - calculateChecksum(); -} - -void IcmpV6Layer::calculateChecksum() -{ - /* Pseudo header of 40 bytes which is composed as follows(in order): - - 16 bytes for the source address - - 16 bytes for the destination address - - 4 bytes big endian payload length(the same value as in the IPv6 header) - - 3 bytes zero + 1 byte nextheader( 58 decimal) big endian - */ - - getIcmpv6Header()->checksum = 0; - - if (m_PrevLayer != nullptr) - { - ScalarBuffer vec[2]; - - vec[0].buffer = (uint16_t *)m_Data; - vec[0].len = m_DataLen; - - const unsigned int pseudoHeaderLen = 40; - const unsigned int bigEndianLen = htobe32(m_DataLen); - const unsigned int bigEndianNextHeader = htobe32(PACKETPP_IPPROTO_ICMPV6); - - uint16_t pseudoHeader[pseudoHeaderLen / 2]; - ((IPv6Layer *)m_PrevLayer)->getSrcIPv6Address().copyTo((uint8_t *)pseudoHeader); - ((IPv6Layer *)m_PrevLayer)->getDstIPv6Address().copyTo((uint8_t *)(pseudoHeader + 8)); - memcpy(&pseudoHeader[16], &bigEndianLen, sizeof(uint32_t)); - memcpy(&pseudoHeader[18], &bigEndianNextHeader, sizeof(uint32_t)); - vec[1].buffer = pseudoHeader; - vec[1].len = pseudoHeaderLen; - - /* Calculate and write checksum */ - getIcmpv6Header()->checksum = htobe16(computeChecksum(vec, 2)); - } -} - -std::string IcmpV6Layer::toString() const -{ - std::ostringstream typeStream; - typeStream << (int)getMessageType(); - return "ICMPv6 Layer, Message type: " + typeStream.str(); -} - -// -// ICMPv6EchoLayer -// - -ICMPv6EchoLayer::ICMPv6EchoLayer(ICMPv6EchoType echoType, uint16_t id, uint16_t sequence, const uint8_t *data, size_t dataLen) -{ - m_DataLen = sizeof(icmpv6_echo_hdr) + dataLen; - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = ICMPv6; - - icmpv6_echo_hdr *header = getEchoHeader(); - - switch (echoType) - { - case REPLY: - header->type = static_cast(ICMPv6MessageType::ICMPv6_ECHO_REPLY); - break; - case REQUEST: - default: - header->type = static_cast(ICMPv6MessageType::ICMPv6_ECHO_REQUEST); - break; - } - - header->code = 0; - header->checksum = 0; - header->id = htobe16(id); - header->sequence = htobe16(sequence); - - if (data != nullptr && dataLen > 0) - memcpy(getEchoDataPtr(), data, dataLen); -} - -uint16_t ICMPv6EchoLayer::getIdentifier() const -{ - return be16toh(getEchoHeader()->id); -} - -uint16_t ICMPv6EchoLayer::getSequenceNr() const -{ - return be16toh(getEchoHeader()->sequence); -} - -std::string ICMPv6EchoLayer::toString() const -{ - std::ostringstream typeStream; - typeStream << (int)getMessageType(); - return "ICMPv6 Layer, Echo Request/Reply Message (type: " + typeStream.str() + ")"; -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModuleIcmpV6Layer + +#include "IcmpV6Layer.h" +#include "EndianPortable.h" +#include "IPv4Layer.h" +#include "IPv6Layer.h" +#include "NdpLayer.h" +#include "PacketUtils.h" +#include "PayloadLayer.h" +#include +#include + +// IcmpV6Layer + +namespace pcpp +{ + +Layer* IcmpV6Layer::parseIcmpV6Layer(uint8_t *data, size_t dataLen, Layer* prevLayer, Packet* packet) +{ + if (dataLen < sizeof(icmpv6hdr)) + return new PayloadLayer(data, dataLen, prevLayer, packet); + + icmpv6hdr *hdr = (icmpv6hdr *)data; + ICMPv6MessageType messageType = static_cast(hdr->type); + + switch (messageType) + { + case ICMPv6MessageType::ICMPv6_ECHO_REQUEST: + case ICMPv6MessageType::ICMPv6_ECHO_REPLY: + return new ICMPv6EchoLayer(data, dataLen, prevLayer, packet); + case ICMPv6MessageType::ICMPv6_NEIGHBOR_SOLICITATION: + return new NDPNeighborSolicitationLayer(data, dataLen, prevLayer, packet); + case ICMPv6MessageType::ICMPv6_NEIGHBOR_ADVERTISEMENT: + return new NDPNeighborAdvertisementLayer(data, dataLen, prevLayer, packet); + case ICMPv6MessageType::ICMPv6_UNKNOWN_MESSAGE: + return new PayloadLayer(data, dataLen, prevLayer, packet); + default: + return new IcmpV6Layer(data, dataLen, prevLayer, packet); + } +} + +IcmpV6Layer::IcmpV6Layer(ICMPv6MessageType msgType, uint8_t code, const uint8_t *data, size_t dataLen) +{ + m_DataLen = sizeof(icmpv6hdr) + dataLen; + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = ICMPv6; + + icmpv6hdr *hdr = (icmpv6hdr *)m_Data; + hdr->type = static_cast(msgType); + hdr->code = code; + + if (data != nullptr && dataLen > 0) + memcpy(m_Data + sizeof(icmpv6hdr), data, dataLen); +} + +ICMPv6MessageType IcmpV6Layer::getMessageType() const +{ + return static_cast(getIcmpv6Header()->type); +} + +uint8_t IcmpV6Layer::getCode() const +{ + return getIcmpv6Header()->code; +} + +uint16_t IcmpV6Layer::getChecksum() const +{ + return be16toh(getIcmpv6Header()->checksum); +} + +void IcmpV6Layer::computeCalculateFields() +{ + calculateChecksum(); +} + +void IcmpV6Layer::calculateChecksum() +{ + /* Pseudo header of 40 bytes which is composed as follows(in order): + - 16 bytes for the source address + - 16 bytes for the destination address + - 4 bytes big endian payload length(the same value as in the IPv6 header) + - 3 bytes zero + 1 byte nextheader( 58 decimal) big endian + */ + + getIcmpv6Header()->checksum = 0; + + if (m_PrevLayer != nullptr) + { + ScalarBuffer vec[2]; + + vec[0].buffer = (uint16_t *)m_Data; + vec[0].len = m_DataLen; + + const unsigned int pseudoHeaderLen = 40; + const unsigned int bigEndianLen = htobe32(m_DataLen); + const unsigned int bigEndianNextHeader = htobe32(PACKETPP_IPPROTO_ICMPV6); + + uint16_t pseudoHeader[pseudoHeaderLen / 2]; + ((IPv6Layer *)m_PrevLayer)->getSrcIPv6Address().copyTo((uint8_t *)pseudoHeader); + ((IPv6Layer *)m_PrevLayer)->getDstIPv6Address().copyTo((uint8_t *)(pseudoHeader + 8)); + memcpy(&pseudoHeader[16], &bigEndianLen, sizeof(uint32_t)); + memcpy(&pseudoHeader[18], &bigEndianNextHeader, sizeof(uint32_t)); + vec[1].buffer = pseudoHeader; + vec[1].len = pseudoHeaderLen; + + /* Calculate and write checksum */ + getIcmpv6Header()->checksum = htobe16(computeChecksum(vec, 2)); + } +} + +std::string IcmpV6Layer::toString() const +{ + std::ostringstream typeStream; + typeStream << (int)getMessageType(); + return "ICMPv6 Layer, Message type: " + typeStream.str(); +} + +// +// ICMPv6EchoLayer +// + +ICMPv6EchoLayer::ICMPv6EchoLayer(ICMPv6EchoType echoType, uint16_t id, uint16_t sequence, const uint8_t *data, size_t dataLen) +{ + m_DataLen = sizeof(icmpv6_echo_hdr) + dataLen; + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = ICMPv6; + + icmpv6_echo_hdr *header = getEchoHeader(); + + switch (echoType) + { + case REPLY: + header->type = static_cast(ICMPv6MessageType::ICMPv6_ECHO_REPLY); + break; + case REQUEST: + default: + header->type = static_cast(ICMPv6MessageType::ICMPv6_ECHO_REQUEST); + break; + } + + header->code = 0; + header->checksum = 0; + header->id = htobe16(id); + header->sequence = htobe16(sequence); + + if (data != nullptr && dataLen > 0) + memcpy(getEchoDataPtr(), data, dataLen); +} + +uint16_t ICMPv6EchoLayer::getIdentifier() const +{ + return be16toh(getEchoHeader()->id); +} + +uint16_t ICMPv6EchoLayer::getSequenceNr() const +{ + return be16toh(getEchoHeader()->sequence); +} + +std::string ICMPv6EchoLayer::toString() const +{ + std::ostringstream typeStream; + typeStream << (int)getMessageType(); + return "ICMPv6 Layer, Echo Request/Reply Message (type: " + typeStream.str() + ")"; +} + +} // namespace pcpp diff --git a/Packet++/src/IgmpLayer.cpp b/Packet++/src/IgmpLayer.cpp index 749d7035db..d1df38e237 100644 --- a/Packet++/src/IgmpLayer.cpp +++ b/Packet++/src/IgmpLayer.cpp @@ -1,554 +1,554 @@ -#define LOG_MODULE PacketLogModuleIgmpLayer - -#include "IgmpLayer.h" -#include "PacketUtils.h" -#include "Logger.h" -#include -#include "EndianPortable.h" - -namespace pcpp -{ - -/************* - * IgmpLayer - *************/ - -IgmpLayer::IgmpLayer(IgmpType type, const IPv4Address& groupAddr, uint8_t maxResponseTime, ProtocolType igmpVer) -{ - m_DataLen = getHeaderSizeByVerAndType(igmpVer, type); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = igmpVer; - - setType(type); - if (groupAddr.isValid()) - setGroupAddress(groupAddr); - - getIgmpHeader()->maxResponseTime = maxResponseTime; -} - -void IgmpLayer::setGroupAddress(const IPv4Address& groupAddr) -{ - igmp_header* hdr = getIgmpHeader(); - hdr->groupAddress = groupAddr.toInt(); -} - -IgmpType IgmpLayer::getType() const -{ - uint8_t type = getIgmpHeader()->type; - if (type < (uint8_t)IgmpType_MembershipQuery || - (type > (uint8_t)IgmpType_LeaveGroup && type < (uint8_t)IgmpType_MulticastTracerouteResponse) || - (type > (uint8_t)IgmpType_MulticastTraceroute && type < (uint8_t)IgmpType_MembershipReportV3) || - (type > (uint8_t)IgmpType_MembershipReportV3 && type < (uint8_t)IgmpType_MulticastRouterAdvertisement) || - type > IgmpType_MulticastRouterTermination) - return IgmpType_Unknown; - - return (IgmpType)type; -} - -void IgmpLayer::setType(IgmpType type) -{ - if (type == IgmpType_Unknown) - return; - - igmp_header* hdr = getIgmpHeader(); - hdr->type = type; -} - -ProtocolType IgmpLayer::getIGMPVerFromData(uint8_t* data, size_t dataLen, bool& isQuery) -{ - isQuery = false; - - if (dataLen < 8 || data == nullptr) - return UnknownProtocol; - - switch ((int)data[0]) - { - case IgmpType_MembershipReportV2: - case IgmpType_LeaveGroup: - return IGMPv2; - case IgmpType_MembershipReportV1: - return IGMPv1; - case IgmpType_MembershipReportV3: - return IGMPv3; - case IgmpType_MembershipQuery: - { - isQuery = true; - - if (dataLen >= sizeof(igmpv3_query_header)) - return IGMPv3; - - if (data[1] == 0) - return IGMPv1; - else - return IGMPv2; - } - default: - return UnknownProtocol; - } -} - -uint16_t IgmpLayer::calculateChecksum() -{ - ScalarBuffer buffer; - buffer.buffer = (uint16_t*)getIgmpHeader(); - buffer.len = getHeaderLen(); - return computeChecksum(&buffer, 1); -} - -size_t IgmpLayer::getHeaderSizeByVerAndType(ProtocolType igmpVer, IgmpType igmpType) const -{ - if (igmpVer == IGMPv1 || igmpVer == IGMPv2) - return sizeof(igmp_header); - - if (igmpVer == IGMPv3) - { - if (igmpType == IgmpType_MembershipQuery) - return sizeof(igmpv3_query_header); - else if (igmpType == IgmpType_MembershipReportV3) - return sizeof(igmpv3_report_header); - } - - return 0; -} - -std::string IgmpLayer::toString() const -{ - std::string igmpVer = ""; - switch (getProtocol()) - { - case IGMPv1: - igmpVer = "1"; - break; - case IGMPv2: - igmpVer = "2"; - break; - default: - igmpVer = "3"; - } - - std::string msgType; - - switch (getType()) - { - case IgmpType_MembershipQuery: - msgType = "Membership Query"; - break; - case IgmpType_MembershipReportV1: - msgType = "Membership Report"; - break; - case IgmpType_DVMRP: - msgType = "DVMRP"; - break; - case IgmpType_P1Mv1: - msgType = "PIMv1"; - break; - case IgmpType_CiscoTrace: - msgType = "Cisco Trace"; - break; - case IgmpType_MembershipReportV2: - msgType = "Membership Report"; - break; - case IgmpType_LeaveGroup: - msgType = "Leave Group"; - break; - case IgmpType_MulticastTracerouteResponse: - msgType = "Multicast Traceroute Response"; - break; - case IgmpType_MulticastTraceroute: - msgType = "Multicast Traceroute"; - break; - case IgmpType_MembershipReportV3: - msgType = "Membership Report"; - break; - case IgmpType_MulticastRouterAdvertisement: - msgType = "Multicast Router Advertisement"; - break; - case IgmpType_MulticastRouterSolicitation: - msgType = "Multicast Router Solicitation"; - break; - case IgmpType_MulticastRouterTermination: - msgType = "Multicast Router Termination"; - break; - default: - msgType = "Unknown"; - break; - } - - std::string result = "IGMPv" + igmpVer + " Layer, " + msgType + " message"; - return result; -} - - - - -/************* - * IgmpV1Layer - *************/ - - -void IgmpV1Layer::computeCalculateFields() -{ - igmp_header* hdr = getIgmpHeader(); - hdr->checksum = 0; - hdr->checksum = htobe16(calculateChecksum()); - hdr->maxResponseTime = 0; -} - - - - - -/************* - * IgmpV2Layer - *************/ - - -void IgmpV2Layer::computeCalculateFields() -{ - igmp_header* hdr = getIgmpHeader(); - hdr->checksum = 0; - hdr->checksum = htobe16(calculateChecksum()); -} - - - - - -/****************** - * IgmpV3QueryLayer - ******************/ - - -IgmpV3QueryLayer::IgmpV3QueryLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : - IgmpLayer(data, dataLen, prevLayer, packet, IGMPv3) -{ -} - -IgmpV3QueryLayer::IgmpV3QueryLayer(const IPv4Address& multicastAddr, uint8_t maxResponseTime, uint8_t s_qrv) : - IgmpLayer(IgmpType_MembershipQuery, multicastAddr, maxResponseTime, IGMPv3) -{ - getIgmpV3QueryHeader()->s_qrv = s_qrv; -} - -uint16_t IgmpV3QueryLayer::getSourceAddressCount() const -{ - return be16toh(getIgmpV3QueryHeader()->numOfSources); -} - -IPv4Address IgmpV3QueryLayer::getSourceAddressAtIndex(int index) const -{ - uint16_t numOfSources = getSourceAddressCount(); - if (index < 0 || index >= numOfSources) - return IPv4Address(); - - // verify numOfRecords is a reasonable number that points to data within the packet - int ptrOffset = index * sizeof(uint32_t) + sizeof(igmpv3_query_header); - if (ptrOffset + sizeof(uint32_t) > getDataLen()) - return IPv4Address(); - - uint8_t* ptr = m_Data + ptrOffset; - return IPv4Address(*(uint32_t*)ptr); -} - -size_t IgmpV3QueryLayer::getHeaderLen() const -{ - uint16_t numOfSources = getSourceAddressCount(); - - int headerLen = numOfSources * sizeof(uint32_t) + sizeof(igmpv3_query_header); - - // verify numOfRecords is a reasonable number that points to data within the packet - if ((size_t)headerLen > getDataLen()) - return getDataLen(); - - return (size_t)headerLen; -} - -void IgmpV3QueryLayer::computeCalculateFields() -{ - igmpv3_query_header* hdr = getIgmpV3QueryHeader(); - hdr->checksum = 0; - hdr->checksum = htobe16(calculateChecksum()); -} - -bool IgmpV3QueryLayer::addSourceAddress(const IPv4Address& addr) -{ - return addSourceAddressAtIndex(addr, getSourceAddressCount()); -} - -bool IgmpV3QueryLayer::addSourceAddressAtIndex(const IPv4Address& addr, int index) -{ - uint16_t sourceAddrCount = getSourceAddressCount(); - - if (index < 0 || index > (int)sourceAddrCount) - { - PCPP_LOG_ERROR("Cannot add source address at index " << index << ", index is out of bounds"); - return false; - } - - size_t offset = sizeof(igmpv3_query_header) + index * sizeof(uint32_t); - if (offset > getHeaderLen()) - { - PCPP_LOG_ERROR("Cannot add source address at index " << index << ", index is out of packet bounds"); - return false; - } - - if (!extendLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Cannot add source address at index " << index << ", didn't manage to extend layer"); - return false; - } - - memcpy(m_Data + offset, addr.toBytes(), sizeof(uint32_t)); - - getIgmpV3QueryHeader()->numOfSources = htobe16(sourceAddrCount+1); - - return true; -} - -bool IgmpV3QueryLayer::removeSourceAddressAtIndex(int index) -{ - uint16_t sourceAddrCount = getSourceAddressCount(); - - if (index < 0 || index > (int)sourceAddrCount-1) - { - PCPP_LOG_ERROR("Cannot remove source address at index " << index << ", index is out of bounds"); - return false; - } - - size_t offset = sizeof(igmpv3_query_header) + index * sizeof(uint32_t); - if (offset >= getHeaderLen()) - { - PCPP_LOG_ERROR("Cannot remove source address at index " << index << ", index is out of packet bounds"); - return false; - } - - if (!shortenLayer(offset, sizeof(uint32_t))) - { - PCPP_LOG_ERROR("Cannot remove source address at index " << index << ", didn't manage to shorten layer"); - return false; - } - - getIgmpV3QueryHeader()->numOfSources = htobe16(sourceAddrCount-1); - - return true; -} - -bool IgmpV3QueryLayer::removeAllSourceAddresses() -{ - size_t offset = sizeof(igmpv3_query_header); - size_t numOfBytesToShorted = getHeaderLen() - offset; - - if (!shortenLayer(offset, numOfBytesToShorted)) - { - PCPP_LOG_ERROR("Cannot remove all source addresses, didn't manage to shorten layer"); - return false; - } - - getIgmpV3QueryHeader()->numOfSources = 0; - - return true; -} - - - - - -/******************* - * IgmpV3ReportLayer - *******************/ - - -uint16_t IgmpV3ReportLayer::getGroupRecordCount() const -{ - return be16toh(getReportHeader()->numOfGroupRecords); -} - -igmpv3_group_record* IgmpV3ReportLayer::getFirstGroupRecord() const -{ - // check if there are group records at all - if (getHeaderLen() <= sizeof(igmpv3_report_header)) - return nullptr; - - uint8_t* curGroupPtr = m_Data + sizeof(igmpv3_report_header); - return (igmpv3_group_record*)curGroupPtr; -} - -igmpv3_group_record* IgmpV3ReportLayer::getNextGroupRecord(igmpv3_group_record* groupRecord) const -{ - if (groupRecord == nullptr) - return nullptr; - - // prev group was the last group - if ((uint8_t*)groupRecord + groupRecord->getRecordLen() - m_Data >= (int)getHeaderLen()) - return nullptr; - - igmpv3_group_record* nextGroup = (igmpv3_group_record*)((uint8_t*)groupRecord + groupRecord->getRecordLen()); - - return nextGroup; -} - -void IgmpV3ReportLayer::computeCalculateFields() -{ - igmpv3_report_header* hdr = getReportHeader(); - hdr->checksum = 0; - hdr->checksum = htobe16(calculateChecksum()); -} - -igmpv3_group_record* IgmpV3ReportLayer::addGroupRecordAt(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses, int offset) -{ - if (offset > (int)getHeaderLen()) - { - PCPP_LOG_ERROR("Cannot add group record, offset is out of layer bounds"); - return nullptr; - } - - size_t groupRecordSize = sizeof(igmpv3_group_record) + sizeof(uint32_t)*sourceAddresses.size(); - - if (!extendLayer(offset, groupRecordSize)) - { - PCPP_LOG_ERROR("Cannot add group record, cannot extend layer"); - return nullptr; - } - - uint8_t* groupRecordBuffer = new uint8_t[groupRecordSize]; - memset(groupRecordBuffer, 0, groupRecordSize); - igmpv3_group_record* newGroupRecord = (igmpv3_group_record*)groupRecordBuffer; - newGroupRecord->multicastAddress = multicastAddress.toInt(); - newGroupRecord->recordType = recordType; - newGroupRecord->auxDataLen = 0; - newGroupRecord->numOfSources = htobe16(sourceAddresses.size()); - - int srcAddrOffset = 0; - for (std::vector::const_iterator iter = sourceAddresses.begin(); iter != sourceAddresses.end(); iter++) - { - memcpy(newGroupRecord->sourceAddresses + srcAddrOffset, iter->toBytes(), sizeof(uint32_t)); - srcAddrOffset += sizeof(uint32_t); - } - - memcpy(m_Data + offset, groupRecordBuffer, groupRecordSize); - - delete[] groupRecordBuffer; - - getReportHeader()->numOfGroupRecords = htobe16(getGroupRecordCount() + 1); - - return (igmpv3_group_record*)(m_Data + offset); -} - -igmpv3_group_record* IgmpV3ReportLayer::addGroupRecord(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses) -{ - return addGroupRecordAt(recordType, multicastAddress, sourceAddresses, (int)getHeaderLen()); -} - -igmpv3_group_record* IgmpV3ReportLayer::addGroupRecordAtIndex(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses, int index) -{ - int groupCnt = (int)getGroupRecordCount(); - - if (index < 0 || index > groupCnt) - { - PCPP_LOG_ERROR("Cannot add group record, index " << index << " out of bounds"); - return nullptr; - } - - size_t offset = sizeof(igmpv3_report_header); - - igmpv3_group_record* curRecord = getFirstGroupRecord(); - for (int i = 0; i < index; i++) - { - if (curRecord == nullptr) - { - PCPP_LOG_ERROR("Cannot add group record, cannot find group record at index " << i); - return nullptr; - } - - offset += curRecord->getRecordLen(); - curRecord = getNextGroupRecord(curRecord); - } - - return addGroupRecordAt(recordType, multicastAddress, sourceAddresses, (int)offset); -} - -bool IgmpV3ReportLayer::removeGroupRecordAtIndex(int index) -{ - int groupCnt = (int)getGroupRecordCount(); - - if (index < 0 || index >= groupCnt) - { - PCPP_LOG_ERROR("Cannot remove group record, index " << index << " is out of bounds"); - return false; - } - - size_t offset = sizeof(igmpv3_report_header); - - igmpv3_group_record* curRecord = getFirstGroupRecord(); - for (int i = 0; i < index; i++) - { - if (curRecord == nullptr) - { - PCPP_LOG_ERROR("Cannot remove group record at index " << index << ", cannot find group record at index " << i); - return false; - } - - offset += curRecord->getRecordLen(); - curRecord = getNextGroupRecord(curRecord); - } - - if (!shortenLayer((int)offset, curRecord->getRecordLen())) - { - PCPP_LOG_ERROR("Cannot remove group record at index " << index << ", cannot shorted layer"); - return false; - } - - getReportHeader()->numOfGroupRecords = htobe16(groupCnt-1); - - return true; -} - -bool IgmpV3ReportLayer::removeAllGroupRecords() -{ - int offset = (int)sizeof(igmpv3_report_header); - - if (!shortenLayer(offset, getHeaderLen()-offset)) - { - PCPP_LOG_ERROR("Cannot remove all group records, cannot shorted layer"); - return false; - } - - getReportHeader()->numOfGroupRecords = 0; - return true; -} - - - - - - -/********************* - * igmpv3_group_record - *********************/ - -uint16_t igmpv3_group_record::getSourceAddressCount() const -{ - return be16toh(numOfSources); -} - -IPv4Address igmpv3_group_record::getSourceAddressAtIndex(int index) const -{ - uint16_t numOfRecords = getSourceAddressCount(); - if (index < 0 || index >= numOfRecords) - return IPv4Address(); - - int offset = index * sizeof(uint32_t); - const uint8_t* ptr = sourceAddresses + offset; - return IPv4Address(*(uint32_t*)ptr); -} - -size_t igmpv3_group_record::getRecordLen() const -{ - uint16_t numOfRecords = getSourceAddressCount(); - - int headerLen = numOfRecords * sizeof(uint32_t) + sizeof(igmpv3_group_record); - return (size_t)headerLen; -} - -} +#define LOG_MODULE PacketLogModuleIgmpLayer + +#include "IgmpLayer.h" +#include "PacketUtils.h" +#include "Logger.h" +#include +#include "EndianPortable.h" + +namespace pcpp +{ + +/************* + * IgmpLayer + *************/ + +IgmpLayer::IgmpLayer(IgmpType type, const IPv4Address& groupAddr, uint8_t maxResponseTime, ProtocolType igmpVer) +{ + m_DataLen = getHeaderSizeByVerAndType(igmpVer, type); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = igmpVer; + + setType(type); + if (groupAddr.isValid()) + setGroupAddress(groupAddr); + + getIgmpHeader()->maxResponseTime = maxResponseTime; +} + +void IgmpLayer::setGroupAddress(const IPv4Address& groupAddr) +{ + igmp_header* hdr = getIgmpHeader(); + hdr->groupAddress = groupAddr.toInt(); +} + +IgmpType IgmpLayer::getType() const +{ + uint8_t type = getIgmpHeader()->type; + if (type < (uint8_t)IgmpType_MembershipQuery || + (type > (uint8_t)IgmpType_LeaveGroup && type < (uint8_t)IgmpType_MulticastTracerouteResponse) || + (type > (uint8_t)IgmpType_MulticastTraceroute && type < (uint8_t)IgmpType_MembershipReportV3) || + (type > (uint8_t)IgmpType_MembershipReportV3 && type < (uint8_t)IgmpType_MulticastRouterAdvertisement) || + type > IgmpType_MulticastRouterTermination) + return IgmpType_Unknown; + + return (IgmpType)type; +} + +void IgmpLayer::setType(IgmpType type) +{ + if (type == IgmpType_Unknown) + return; + + igmp_header* hdr = getIgmpHeader(); + hdr->type = type; +} + +ProtocolType IgmpLayer::getIGMPVerFromData(uint8_t* data, size_t dataLen, bool& isQuery) +{ + isQuery = false; + + if (dataLen < 8 || data == nullptr) + return UnknownProtocol; + + switch ((int)data[0]) + { + case IgmpType_MembershipReportV2: + case IgmpType_LeaveGroup: + return IGMPv2; + case IgmpType_MembershipReportV1: + return IGMPv1; + case IgmpType_MembershipReportV3: + return IGMPv3; + case IgmpType_MembershipQuery: + { + isQuery = true; + + if (dataLen >= sizeof(igmpv3_query_header)) + return IGMPv3; + + if (data[1] == 0) + return IGMPv1; + else + return IGMPv2; + } + default: + return UnknownProtocol; + } +} + +uint16_t IgmpLayer::calculateChecksum() +{ + ScalarBuffer buffer; + buffer.buffer = (uint16_t*)getIgmpHeader(); + buffer.len = getHeaderLen(); + return computeChecksum(&buffer, 1); +} + +size_t IgmpLayer::getHeaderSizeByVerAndType(ProtocolType igmpVer, IgmpType igmpType) const +{ + if (igmpVer == IGMPv1 || igmpVer == IGMPv2) + return sizeof(igmp_header); + + if (igmpVer == IGMPv3) + { + if (igmpType == IgmpType_MembershipQuery) + return sizeof(igmpv3_query_header); + else if (igmpType == IgmpType_MembershipReportV3) + return sizeof(igmpv3_report_header); + } + + return 0; +} + +std::string IgmpLayer::toString() const +{ + std::string igmpVer = ""; + switch (getProtocol()) + { + case IGMPv1: + igmpVer = "1"; + break; + case IGMPv2: + igmpVer = "2"; + break; + default: + igmpVer = "3"; + } + + std::string msgType; + + switch (getType()) + { + case IgmpType_MembershipQuery: + msgType = "Membership Query"; + break; + case IgmpType_MembershipReportV1: + msgType = "Membership Report"; + break; + case IgmpType_DVMRP: + msgType = "DVMRP"; + break; + case IgmpType_P1Mv1: + msgType = "PIMv1"; + break; + case IgmpType_CiscoTrace: + msgType = "Cisco Trace"; + break; + case IgmpType_MembershipReportV2: + msgType = "Membership Report"; + break; + case IgmpType_LeaveGroup: + msgType = "Leave Group"; + break; + case IgmpType_MulticastTracerouteResponse: + msgType = "Multicast Traceroute Response"; + break; + case IgmpType_MulticastTraceroute: + msgType = "Multicast Traceroute"; + break; + case IgmpType_MembershipReportV3: + msgType = "Membership Report"; + break; + case IgmpType_MulticastRouterAdvertisement: + msgType = "Multicast Router Advertisement"; + break; + case IgmpType_MulticastRouterSolicitation: + msgType = "Multicast Router Solicitation"; + break; + case IgmpType_MulticastRouterTermination: + msgType = "Multicast Router Termination"; + break; + default: + msgType = "Unknown"; + break; + } + + std::string result = "IGMPv" + igmpVer + " Layer, " + msgType + " message"; + return result; +} + + + + +/************* + * IgmpV1Layer + *************/ + + +void IgmpV1Layer::computeCalculateFields() +{ + igmp_header* hdr = getIgmpHeader(); + hdr->checksum = 0; + hdr->checksum = htobe16(calculateChecksum()); + hdr->maxResponseTime = 0; +} + + + + + +/************* + * IgmpV2Layer + *************/ + + +void IgmpV2Layer::computeCalculateFields() +{ + igmp_header* hdr = getIgmpHeader(); + hdr->checksum = 0; + hdr->checksum = htobe16(calculateChecksum()); +} + + + + + +/****************** + * IgmpV3QueryLayer + ******************/ + + +IgmpV3QueryLayer::IgmpV3QueryLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : + IgmpLayer(data, dataLen, prevLayer, packet, IGMPv3) +{ +} + +IgmpV3QueryLayer::IgmpV3QueryLayer(const IPv4Address& multicastAddr, uint8_t maxResponseTime, uint8_t s_qrv) : + IgmpLayer(IgmpType_MembershipQuery, multicastAddr, maxResponseTime, IGMPv3) +{ + getIgmpV3QueryHeader()->s_qrv = s_qrv; +} + +uint16_t IgmpV3QueryLayer::getSourceAddressCount() const +{ + return be16toh(getIgmpV3QueryHeader()->numOfSources); +} + +IPv4Address IgmpV3QueryLayer::getSourceAddressAtIndex(int index) const +{ + uint16_t numOfSources = getSourceAddressCount(); + if (index < 0 || index >= numOfSources) + return IPv4Address(); + + // verify numOfRecords is a reasonable number that points to data within the packet + int ptrOffset = index * sizeof(uint32_t) + sizeof(igmpv3_query_header); + if (ptrOffset + sizeof(uint32_t) > getDataLen()) + return IPv4Address(); + + uint8_t* ptr = m_Data + ptrOffset; + return IPv4Address(*(uint32_t*)ptr); +} + +size_t IgmpV3QueryLayer::getHeaderLen() const +{ + uint16_t numOfSources = getSourceAddressCount(); + + int headerLen = numOfSources * sizeof(uint32_t) + sizeof(igmpv3_query_header); + + // verify numOfRecords is a reasonable number that points to data within the packet + if ((size_t)headerLen > getDataLen()) + return getDataLen(); + + return (size_t)headerLen; +} + +void IgmpV3QueryLayer::computeCalculateFields() +{ + igmpv3_query_header* hdr = getIgmpV3QueryHeader(); + hdr->checksum = 0; + hdr->checksum = htobe16(calculateChecksum()); +} + +bool IgmpV3QueryLayer::addSourceAddress(const IPv4Address& addr) +{ + return addSourceAddressAtIndex(addr, getSourceAddressCount()); +} + +bool IgmpV3QueryLayer::addSourceAddressAtIndex(const IPv4Address& addr, int index) +{ + uint16_t sourceAddrCount = getSourceAddressCount(); + + if (index < 0 || index > (int)sourceAddrCount) + { + PCPP_LOG_ERROR("Cannot add source address at index " << index << ", index is out of bounds"); + return false; + } + + size_t offset = sizeof(igmpv3_query_header) + index * sizeof(uint32_t); + if (offset > getHeaderLen()) + { + PCPP_LOG_ERROR("Cannot add source address at index " << index << ", index is out of packet bounds"); + return false; + } + + if (!extendLayer(offset, sizeof(uint32_t))) + { + PCPP_LOG_ERROR("Cannot add source address at index " << index << ", didn't manage to extend layer"); + return false; + } + + memcpy(m_Data + offset, addr.toBytes(), sizeof(uint32_t)); + + getIgmpV3QueryHeader()->numOfSources = htobe16(sourceAddrCount+1); + + return true; +} + +bool IgmpV3QueryLayer::removeSourceAddressAtIndex(int index) +{ + uint16_t sourceAddrCount = getSourceAddressCount(); + + if (index < 0 || index > (int)sourceAddrCount-1) + { + PCPP_LOG_ERROR("Cannot remove source address at index " << index << ", index is out of bounds"); + return false; + } + + size_t offset = sizeof(igmpv3_query_header) + index * sizeof(uint32_t); + if (offset >= getHeaderLen()) + { + PCPP_LOG_ERROR("Cannot remove source address at index " << index << ", index is out of packet bounds"); + return false; + } + + if (!shortenLayer(offset, sizeof(uint32_t))) + { + PCPP_LOG_ERROR("Cannot remove source address at index " << index << ", didn't manage to shorten layer"); + return false; + } + + getIgmpV3QueryHeader()->numOfSources = htobe16(sourceAddrCount-1); + + return true; +} + +bool IgmpV3QueryLayer::removeAllSourceAddresses() +{ + size_t offset = sizeof(igmpv3_query_header); + size_t numOfBytesToShorted = getHeaderLen() - offset; + + if (!shortenLayer(offset, numOfBytesToShorted)) + { + PCPP_LOG_ERROR("Cannot remove all source addresses, didn't manage to shorten layer"); + return false; + } + + getIgmpV3QueryHeader()->numOfSources = 0; + + return true; +} + + + + + +/******************* + * IgmpV3ReportLayer + *******************/ + + +uint16_t IgmpV3ReportLayer::getGroupRecordCount() const +{ + return be16toh(getReportHeader()->numOfGroupRecords); +} + +igmpv3_group_record* IgmpV3ReportLayer::getFirstGroupRecord() const +{ + // check if there are group records at all + if (getHeaderLen() <= sizeof(igmpv3_report_header)) + return nullptr; + + uint8_t* curGroupPtr = m_Data + sizeof(igmpv3_report_header); + return (igmpv3_group_record*)curGroupPtr; +} + +igmpv3_group_record* IgmpV3ReportLayer::getNextGroupRecord(igmpv3_group_record* groupRecord) const +{ + if (groupRecord == nullptr) + return nullptr; + + // prev group was the last group + if ((uint8_t*)groupRecord + groupRecord->getRecordLen() - m_Data >= (int)getHeaderLen()) + return nullptr; + + igmpv3_group_record* nextGroup = (igmpv3_group_record*)((uint8_t*)groupRecord + groupRecord->getRecordLen()); + + return nextGroup; +} + +void IgmpV3ReportLayer::computeCalculateFields() +{ + igmpv3_report_header* hdr = getReportHeader(); + hdr->checksum = 0; + hdr->checksum = htobe16(calculateChecksum()); +} + +igmpv3_group_record* IgmpV3ReportLayer::addGroupRecordAt(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses, int offset) +{ + if (offset > (int)getHeaderLen()) + { + PCPP_LOG_ERROR("Cannot add group record, offset is out of layer bounds"); + return nullptr; + } + + size_t groupRecordSize = sizeof(igmpv3_group_record) + sizeof(uint32_t)*sourceAddresses.size(); + + if (!extendLayer(offset, groupRecordSize)) + { + PCPP_LOG_ERROR("Cannot add group record, cannot extend layer"); + return nullptr; + } + + uint8_t* groupRecordBuffer = new uint8_t[groupRecordSize]; + memset(groupRecordBuffer, 0, groupRecordSize); + igmpv3_group_record* newGroupRecord = (igmpv3_group_record*)groupRecordBuffer; + newGroupRecord->multicastAddress = multicastAddress.toInt(); + newGroupRecord->recordType = recordType; + newGroupRecord->auxDataLen = 0; + newGroupRecord->numOfSources = htobe16(sourceAddresses.size()); + + int srcAddrOffset = 0; + for (std::vector::const_iterator iter = sourceAddresses.begin(); iter != sourceAddresses.end(); iter++) + { + memcpy(newGroupRecord->sourceAddresses + srcAddrOffset, iter->toBytes(), sizeof(uint32_t)); + srcAddrOffset += sizeof(uint32_t); + } + + memcpy(m_Data + offset, groupRecordBuffer, groupRecordSize); + + delete[] groupRecordBuffer; + + getReportHeader()->numOfGroupRecords = htobe16(getGroupRecordCount() + 1); + + return (igmpv3_group_record*)(m_Data + offset); +} + +igmpv3_group_record* IgmpV3ReportLayer::addGroupRecord(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses) +{ + return addGroupRecordAt(recordType, multicastAddress, sourceAddresses, (int)getHeaderLen()); +} + +igmpv3_group_record* IgmpV3ReportLayer::addGroupRecordAtIndex(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector& sourceAddresses, int index) +{ + int groupCnt = (int)getGroupRecordCount(); + + if (index < 0 || index > groupCnt) + { + PCPP_LOG_ERROR("Cannot add group record, index " << index << " out of bounds"); + return nullptr; + } + + size_t offset = sizeof(igmpv3_report_header); + + igmpv3_group_record* curRecord = getFirstGroupRecord(); + for (int i = 0; i < index; i++) + { + if (curRecord == nullptr) + { + PCPP_LOG_ERROR("Cannot add group record, cannot find group record at index " << i); + return nullptr; + } + + offset += curRecord->getRecordLen(); + curRecord = getNextGroupRecord(curRecord); + } + + return addGroupRecordAt(recordType, multicastAddress, sourceAddresses, (int)offset); +} + +bool IgmpV3ReportLayer::removeGroupRecordAtIndex(int index) +{ + int groupCnt = (int)getGroupRecordCount(); + + if (index < 0 || index >= groupCnt) + { + PCPP_LOG_ERROR("Cannot remove group record, index " << index << " is out of bounds"); + return false; + } + + size_t offset = sizeof(igmpv3_report_header); + + igmpv3_group_record* curRecord = getFirstGroupRecord(); + for (int i = 0; i < index; i++) + { + if (curRecord == nullptr) + { + PCPP_LOG_ERROR("Cannot remove group record at index " << index << ", cannot find group record at index " << i); + return false; + } + + offset += curRecord->getRecordLen(); + curRecord = getNextGroupRecord(curRecord); + } + + if (!shortenLayer((int)offset, curRecord->getRecordLen())) + { + PCPP_LOG_ERROR("Cannot remove group record at index " << index << ", cannot shorted layer"); + return false; + } + + getReportHeader()->numOfGroupRecords = htobe16(groupCnt-1); + + return true; +} + +bool IgmpV3ReportLayer::removeAllGroupRecords() +{ + int offset = (int)sizeof(igmpv3_report_header); + + if (!shortenLayer(offset, getHeaderLen()-offset)) + { + PCPP_LOG_ERROR("Cannot remove all group records, cannot shorted layer"); + return false; + } + + getReportHeader()->numOfGroupRecords = 0; + return true; +} + + + + + + +/********************* + * igmpv3_group_record + *********************/ + +uint16_t igmpv3_group_record::getSourceAddressCount() const +{ + return be16toh(numOfSources); +} + +IPv4Address igmpv3_group_record::getSourceAddressAtIndex(int index) const +{ + uint16_t numOfRecords = getSourceAddressCount(); + if (index < 0 || index >= numOfRecords) + return IPv4Address(); + + int offset = index * sizeof(uint32_t); + const uint8_t* ptr = sourceAddresses + offset; + return IPv4Address(*(uint32_t*)ptr); +} + +size_t igmpv3_group_record::getRecordLen() const +{ + uint16_t numOfRecords = getSourceAddressCount(); + + int headerLen = numOfRecords * sizeof(uint32_t) + sizeof(igmpv3_group_record); + return (size_t)headerLen; +} + +} diff --git a/Packet++/src/Layer.cpp b/Packet++/src/Layer.cpp index 72775e6469..f2b45542fc 100644 --- a/Packet++/src/Layer.cpp +++ b/Packet++/src/Layer.cpp @@ -1,105 +1,105 @@ -#define LOG_MODULE PacketLogModuleLayer - -#include "Layer.h" -#include -#include "Logger.h" -#include "Packet.h" - -namespace pcpp -{ - -Layer::~Layer() -{ - if (!isAllocatedToPacket()) - delete [] m_Data; -} - -Layer::Layer(const Layer& other) : m_Packet(nullptr), m_Protocol(other.m_Protocol), m_NextLayer(nullptr), m_PrevLayer(nullptr), m_IsAllocatedInPacket(false) -{ - m_DataLen = other.getHeaderLen(); - m_Data = new uint8_t[other.m_DataLen]; - memcpy(m_Data, other.m_Data, other.m_DataLen); -} - -Layer& Layer::operator=(const Layer& other) -{ - if (this == &other) - return *this; - - if (m_Data != nullptr) - delete [] m_Data; - - m_DataLen = other.getHeaderLen(); - m_Packet = nullptr; - m_Protocol = other.m_Protocol; - m_NextLayer = nullptr; - m_PrevLayer = nullptr; - m_Data = new uint8_t[other.m_DataLen]; - m_IsAllocatedInPacket = false; - memcpy(m_Data, other.m_Data, other.m_DataLen); - - return *this; -} - -void Layer::copyData(uint8_t* toArr) const -{ - memcpy(toArr, m_Data, m_DataLen); -} - -bool Layer::extendLayer(int offsetInLayer, size_t numOfBytesToExtend) -{ - if (m_Data == nullptr) - { - PCPP_LOG_ERROR("Layer's data is NULL"); - return false; - } - - if (m_Packet == nullptr) - { - if ((size_t)offsetInLayer > m_DataLen) - { - PCPP_LOG_ERROR("Requested offset is larger than data length"); - return false; - } - - uint8_t* newData = new uint8_t[m_DataLen + numOfBytesToExtend]; - memcpy(newData, m_Data, offsetInLayer); - memcpy(newData + offsetInLayer + numOfBytesToExtend, m_Data + offsetInLayer, m_DataLen - offsetInLayer); - delete [] m_Data; - m_Data = newData; - m_DataLen += numOfBytesToExtend; - return true; - } - - return m_Packet->extendLayer(this, offsetInLayer, numOfBytesToExtend); -} - -bool Layer::shortenLayer(int offsetInLayer, size_t numOfBytesToShorten) -{ - if (m_Data == nullptr) - { - PCPP_LOG_ERROR("Layer's data is NULL"); - return false; - } - - if (m_Packet == nullptr) - { - if ((size_t)offsetInLayer >= m_DataLen) - { - PCPP_LOG_ERROR("Requested offset is larger than data length"); - return false; - } - - uint8_t* newData = new uint8_t[m_DataLen - numOfBytesToShorten]; - memcpy(newData, m_Data, offsetInLayer); - memcpy(newData + offsetInLayer, m_Data + offsetInLayer + numOfBytesToShorten, m_DataLen - offsetInLayer - numOfBytesToShorten); - delete [] m_Data; - m_Data = newData; - m_DataLen -= numOfBytesToShorten; - return true; - } - - return m_Packet->shortenLayer(this, offsetInLayer, numOfBytesToShorten); -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModuleLayer + +#include "Layer.h" +#include +#include "Logger.h" +#include "Packet.h" + +namespace pcpp +{ + +Layer::~Layer() +{ + if (!isAllocatedToPacket()) + delete [] m_Data; +} + +Layer::Layer(const Layer& other) : m_Packet(nullptr), m_Protocol(other.m_Protocol), m_NextLayer(nullptr), m_PrevLayer(nullptr), m_IsAllocatedInPacket(false) +{ + m_DataLen = other.getHeaderLen(); + m_Data = new uint8_t[other.m_DataLen]; + memcpy(m_Data, other.m_Data, other.m_DataLen); +} + +Layer& Layer::operator=(const Layer& other) +{ + if (this == &other) + return *this; + + if (m_Data != nullptr) + delete [] m_Data; + + m_DataLen = other.getHeaderLen(); + m_Packet = nullptr; + m_Protocol = other.m_Protocol; + m_NextLayer = nullptr; + m_PrevLayer = nullptr; + m_Data = new uint8_t[other.m_DataLen]; + m_IsAllocatedInPacket = false; + memcpy(m_Data, other.m_Data, other.m_DataLen); + + return *this; +} + +void Layer::copyData(uint8_t* toArr) const +{ + memcpy(toArr, m_Data, m_DataLen); +} + +bool Layer::extendLayer(int offsetInLayer, size_t numOfBytesToExtend) +{ + if (m_Data == nullptr) + { + PCPP_LOG_ERROR("Layer's data is NULL"); + return false; + } + + if (m_Packet == nullptr) + { + if ((size_t)offsetInLayer > m_DataLen) + { + PCPP_LOG_ERROR("Requested offset is larger than data length"); + return false; + } + + uint8_t* newData = new uint8_t[m_DataLen + numOfBytesToExtend]; + memcpy(newData, m_Data, offsetInLayer); + memcpy(newData + offsetInLayer + numOfBytesToExtend, m_Data + offsetInLayer, m_DataLen - offsetInLayer); + delete [] m_Data; + m_Data = newData; + m_DataLen += numOfBytesToExtend; + return true; + } + + return m_Packet->extendLayer(this, offsetInLayer, numOfBytesToExtend); +} + +bool Layer::shortenLayer(int offsetInLayer, size_t numOfBytesToShorten) +{ + if (m_Data == nullptr) + { + PCPP_LOG_ERROR("Layer's data is NULL"); + return false; + } + + if (m_Packet == nullptr) + { + if ((size_t)offsetInLayer >= m_DataLen) + { + PCPP_LOG_ERROR("Requested offset is larger than data length"); + return false; + } + + uint8_t* newData = new uint8_t[m_DataLen - numOfBytesToShorten]; + memcpy(newData, m_Data, offsetInLayer); + memcpy(newData + offsetInLayer, m_Data + offsetInLayer + numOfBytesToShorten, m_DataLen - offsetInLayer - numOfBytesToShorten); + delete [] m_Data; + m_Data = newData; + m_DataLen -= numOfBytesToShorten; + return true; + } + + return m_Packet->shortenLayer(this, offsetInLayer, numOfBytesToShorten); +} + +} // namespace pcpp diff --git a/Packet++/src/NdpLayer.cpp b/Packet++/src/NdpLayer.cpp index b952875f84..29853f1e27 100644 --- a/Packet++/src/NdpLayer.cpp +++ b/Packet++/src/NdpLayer.cpp @@ -1,219 +1,219 @@ -#define LOG_MODULE PacketLogModuleNdpLayer - -#include "NdpLayer.h" -#include "Logger.h" - -namespace pcpp -{ - -/* - * NdpOptionBuilder - */ - -NdpOption NdpOptionBuilder::build() const -{ - size_t optionSize = m_RecValueLen + 2 * sizeof(uint8_t); - size_t padding = (8 - (optionSize % 8)) % 8; // Padding bytes for a option with 8 byte boundary - size_t optionSizeWithPadding = optionSize + padding; - - uint8_t *recordBuffer = new uint8_t[optionSizeWithPadding]; - memset(recordBuffer, 0, optionSizeWithPadding); - recordBuffer[0] = static_cast(m_RecType); - recordBuffer[1] = static_cast(optionSizeWithPadding / 8); // length value is stored in units of 8 octets - memcpy(recordBuffer + 2, m_RecValue, m_RecValueLen); - - return NdpOption(recordBuffer); -} - -/* - * NDPLayerBase - */ - -size_t NDPLayerBase::getNdpOptionCount() const -{ - return m_OptionReader.getTLVRecordCount(getNdpOptionsBasePtr(), getHeaderLen() - getNdpHeaderLen()); -} - -NdpOption NDPLayerBase::getFirstNdpOption() const -{ - return m_OptionReader.getFirstTLVRecord(getNdpOptionsBasePtr(), getHeaderLen() - getNdpHeaderLen()); -} - -NdpOption NDPLayerBase::getNextNdpOption(NdpOption &option) const -{ - return m_OptionReader.getNextTLVRecord(option, getNdpOptionsBasePtr(), getHeaderLen() - getNdpHeaderLen()); -} - -NdpOption NDPLayerBase::getNdpOption(NDPNeighborOptionTypes option) const -{ - return m_OptionReader.getTLVRecord((uint8_t)option, getNdpOptionsBasePtr(), getHeaderLen() - getNdpHeaderLen()); -} - -NdpOption NDPLayerBase::addNdpOption(const NdpOptionBuilder &optionBuilder) -{ - return addNdpOptionAt(optionBuilder, getHeaderLen()); -} - -NdpOption NDPLayerBase::addNdpOptionAt(const NdpOptionBuilder &optionBuilder, int offset) -{ - NdpOption newOption = optionBuilder.build(); - - if (newOption.isNull()) - { - PCPP_LOG_ERROR("Cannot build new option of type " << (int)newOption.getType()); - return newOption; - } - - size_t sizeToExtend = newOption.getTotalSize(); - - if (!extendLayer(offset, sizeToExtend)) - { - PCPP_LOG_ERROR("Could not extend NdpLayer in [" << sizeToExtend << "] bytes"); - newOption.purgeRecordData(); - return NdpOption(nullptr); - } - - memcpy(m_Data + offset, newOption.getRecordBasePtr(), newOption.getTotalSize()); - - newOption.purgeRecordData(); - - m_OptionReader.changeTLVRecordCount(1); - - uint8_t *newOptPtr = m_Data + offset; - - return NdpOption(newOptPtr); -} - -bool NDPLayerBase::removeAllNdpOptions() -{ - int offset = getNdpHeaderLen(); - if (!shortenLayer(offset, getHeaderLen() - offset)) - return false; - - m_OptionReader.changeTLVRecordCount(0 - getNdpOptionCount()); - return true; -} - -/* - * NDPNeighborSolicitationLayer - */ - -NDPNeighborSolicitationLayer::NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address &targetIP) -{ - initLayer(code, targetIP); -} - -NDPNeighborSolicitationLayer::NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address &targetIP, - const MacAddress &srcMac) -{ - initLayer(code, targetIP); - this->addNdpOption( - pcpp::NdpOptionBuilder(pcpp::NDPNeighborOptionTypes::NDP_OPTION_SOURCE_LINK_LAYER, srcMac.getRawData(), 6)); -} - -void NDPNeighborSolicitationLayer::initLayer(uint8_t code, const IPv6Address &targetIP) -{ - m_DataLen = sizeof(ndpneighborsolicitationhdr); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = ICMPv6; - - ndpneighborsolicitationhdr *pHdr = getNdpHeader(); - pHdr->type = static_cast(ICMPv6MessageType::ICMPv6_NEIGHBOR_SOLICITATION); - pHdr->code = code; - memcpy(pHdr->targetIP, targetIP.toBytes(), 16); -} - -bool NDPNeighborSolicitationLayer::hasLinkLayerAddress() const -{ - NdpOption option = this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_SOURCE_LINK_LAYER); - return option.isNull() ? false : true; -} - -MacAddress NDPNeighborSolicitationLayer::getLinkLayerAddress() const -{ - NdpOption option = this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_SOURCE_LINK_LAYER); - - if (option.isNull()) - { - return MacAddress::Zero; - } - - return MacAddress(option.getValue()); -} - -std::string NDPNeighborSolicitationLayer::toString() const -{ - std::ostringstream typeStream; - typeStream << "ICMPv6 Layer, Neighbor Solicitation Message, TargetIP: " + getTargetIP().toString(); - hasLinkLayerAddress() ? typeStream << ", SourceMAC: " + getLinkLayerAddress().toString() : typeStream << ", no Option"; - - return typeStream.str(); - -} - -/* - * NDPNeighborAdvertisementLayer - */ - -NDPNeighborAdvertisementLayer::NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address &targetIP, - const MacAddress &targetMac, bool routerFlag, - bool unicastFlag, bool overrideFlag) -{ - initLayer(code, targetIP, routerFlag, unicastFlag, overrideFlag); - this->addNdpOption( - pcpp::NdpOptionBuilder(pcpp::NDPNeighborOptionTypes::NDP_OPTION_TARGET_LINK_LAYER, targetMac.getRawData(), 6)); -} - -NDPNeighborAdvertisementLayer::NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address &targetIP, bool routerFlag, - bool unicastFlag, bool overrideFlag) -{ - initLayer(code, targetIP, routerFlag, unicastFlag, overrideFlag); -} - -void NDPNeighborAdvertisementLayer::initLayer(uint8_t code, const IPv6Address &targetIP, bool routerFlag, - bool unicastFlag, bool overrideFlag) -{ - m_DataLen = sizeof(ndpneighboradvertisementhdr); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = ICMPv6; - - ndpneighboradvertisementhdr *pHdr = getNdpHeader(); - pHdr->type = static_cast(ICMPv6MessageType::ICMPv6_NEIGHBOR_ADVERTISEMENT); - pHdr->code = code; - pHdr->router = routerFlag; - pHdr->solicited = unicastFlag; - pHdr->override = overrideFlag; - - memcpy(pHdr->targetIP, targetIP.toBytes(), 16); -} - -bool NDPNeighborAdvertisementLayer::hasTargetMacInfo() const -{ - NdpOption option = this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_TARGET_LINK_LAYER); - return option.isNull() ? false : true; -} - -MacAddress NDPNeighborAdvertisementLayer::getTargetMac() const -{ - NdpOption option = this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_TARGET_LINK_LAYER); - - if (option.isNull()) - { - return MacAddress::Zero; - } - - return MacAddress(option.getValue()); -} - -std::string NDPNeighborAdvertisementLayer::toString() const -{ - std::ostringstream typeStream; - typeStream << "ICMPv6 Layer, Neighbor Advertisement Message, TargetIP: " << getTargetIP().toString(); - hasTargetMacInfo() ? typeStream << ", TargetMAC: " + getTargetMac().toString() : typeStream << ", no Option"; - - return typeStream.str(); -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModuleNdpLayer + +#include "NdpLayer.h" +#include "Logger.h" + +namespace pcpp +{ + +/* + * NdpOptionBuilder + */ + +NdpOption NdpOptionBuilder::build() const +{ + size_t optionSize = m_RecValueLen + 2 * sizeof(uint8_t); + size_t padding = (8 - (optionSize % 8)) % 8; // Padding bytes for a option with 8 byte boundary + size_t optionSizeWithPadding = optionSize + padding; + + uint8_t *recordBuffer = new uint8_t[optionSizeWithPadding]; + memset(recordBuffer, 0, optionSizeWithPadding); + recordBuffer[0] = static_cast(m_RecType); + recordBuffer[1] = static_cast(optionSizeWithPadding / 8); // length value is stored in units of 8 octets + memcpy(recordBuffer + 2, m_RecValue, m_RecValueLen); + + return NdpOption(recordBuffer); +} + +/* + * NDPLayerBase + */ + +size_t NDPLayerBase::getNdpOptionCount() const +{ + return m_OptionReader.getTLVRecordCount(getNdpOptionsBasePtr(), getHeaderLen() - getNdpHeaderLen()); +} + +NdpOption NDPLayerBase::getFirstNdpOption() const +{ + return m_OptionReader.getFirstTLVRecord(getNdpOptionsBasePtr(), getHeaderLen() - getNdpHeaderLen()); +} + +NdpOption NDPLayerBase::getNextNdpOption(NdpOption &option) const +{ + return m_OptionReader.getNextTLVRecord(option, getNdpOptionsBasePtr(), getHeaderLen() - getNdpHeaderLen()); +} + +NdpOption NDPLayerBase::getNdpOption(NDPNeighborOptionTypes option) const +{ + return m_OptionReader.getTLVRecord((uint8_t)option, getNdpOptionsBasePtr(), getHeaderLen() - getNdpHeaderLen()); +} + +NdpOption NDPLayerBase::addNdpOption(const NdpOptionBuilder &optionBuilder) +{ + return addNdpOptionAt(optionBuilder, getHeaderLen()); +} + +NdpOption NDPLayerBase::addNdpOptionAt(const NdpOptionBuilder &optionBuilder, int offset) +{ + NdpOption newOption = optionBuilder.build(); + + if (newOption.isNull()) + { + PCPP_LOG_ERROR("Cannot build new option of type " << (int)newOption.getType()); + return newOption; + } + + size_t sizeToExtend = newOption.getTotalSize(); + + if (!extendLayer(offset, sizeToExtend)) + { + PCPP_LOG_ERROR("Could not extend NdpLayer in [" << sizeToExtend << "] bytes"); + newOption.purgeRecordData(); + return NdpOption(nullptr); + } + + memcpy(m_Data + offset, newOption.getRecordBasePtr(), newOption.getTotalSize()); + + newOption.purgeRecordData(); + + m_OptionReader.changeTLVRecordCount(1); + + uint8_t *newOptPtr = m_Data + offset; + + return NdpOption(newOptPtr); +} + +bool NDPLayerBase::removeAllNdpOptions() +{ + int offset = getNdpHeaderLen(); + if (!shortenLayer(offset, getHeaderLen() - offset)) + return false; + + m_OptionReader.changeTLVRecordCount(0 - getNdpOptionCount()); + return true; +} + +/* + * NDPNeighborSolicitationLayer + */ + +NDPNeighborSolicitationLayer::NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address &targetIP) +{ + initLayer(code, targetIP); +} + +NDPNeighborSolicitationLayer::NDPNeighborSolicitationLayer(uint8_t code, const IPv6Address &targetIP, + const MacAddress &srcMac) +{ + initLayer(code, targetIP); + this->addNdpOption( + pcpp::NdpOptionBuilder(pcpp::NDPNeighborOptionTypes::NDP_OPTION_SOURCE_LINK_LAYER, srcMac.getRawData(), 6)); +} + +void NDPNeighborSolicitationLayer::initLayer(uint8_t code, const IPv6Address &targetIP) +{ + m_DataLen = sizeof(ndpneighborsolicitationhdr); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = ICMPv6; + + ndpneighborsolicitationhdr *pHdr = getNdpHeader(); + pHdr->type = static_cast(ICMPv6MessageType::ICMPv6_NEIGHBOR_SOLICITATION); + pHdr->code = code; + memcpy(pHdr->targetIP, targetIP.toBytes(), 16); +} + +bool NDPNeighborSolicitationLayer::hasLinkLayerAddress() const +{ + NdpOption option = this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_SOURCE_LINK_LAYER); + return option.isNull() ? false : true; +} + +MacAddress NDPNeighborSolicitationLayer::getLinkLayerAddress() const +{ + NdpOption option = this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_SOURCE_LINK_LAYER); + + if (option.isNull()) + { + return MacAddress::Zero; + } + + return MacAddress(option.getValue()); +} + +std::string NDPNeighborSolicitationLayer::toString() const +{ + std::ostringstream typeStream; + typeStream << "ICMPv6 Layer, Neighbor Solicitation Message, TargetIP: " + getTargetIP().toString(); + hasLinkLayerAddress() ? typeStream << ", SourceMAC: " + getLinkLayerAddress().toString() : typeStream << ", no Option"; + + return typeStream.str(); + +} + +/* + * NDPNeighborAdvertisementLayer + */ + +NDPNeighborAdvertisementLayer::NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address &targetIP, + const MacAddress &targetMac, bool routerFlag, + bool unicastFlag, bool overrideFlag) +{ + initLayer(code, targetIP, routerFlag, unicastFlag, overrideFlag); + this->addNdpOption( + pcpp::NdpOptionBuilder(pcpp::NDPNeighborOptionTypes::NDP_OPTION_TARGET_LINK_LAYER, targetMac.getRawData(), 6)); +} + +NDPNeighborAdvertisementLayer::NDPNeighborAdvertisementLayer(uint8_t code, const IPv6Address &targetIP, bool routerFlag, + bool unicastFlag, bool overrideFlag) +{ + initLayer(code, targetIP, routerFlag, unicastFlag, overrideFlag); +} + +void NDPNeighborAdvertisementLayer::initLayer(uint8_t code, const IPv6Address &targetIP, bool routerFlag, + bool unicastFlag, bool overrideFlag) +{ + m_DataLen = sizeof(ndpneighboradvertisementhdr); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = ICMPv6; + + ndpneighboradvertisementhdr *pHdr = getNdpHeader(); + pHdr->type = static_cast(ICMPv6MessageType::ICMPv6_NEIGHBOR_ADVERTISEMENT); + pHdr->code = code; + pHdr->router = routerFlag; + pHdr->solicited = unicastFlag; + pHdr->override = overrideFlag; + + memcpy(pHdr->targetIP, targetIP.toBytes(), 16); +} + +bool NDPNeighborAdvertisementLayer::hasTargetMacInfo() const +{ + NdpOption option = this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_TARGET_LINK_LAYER); + return option.isNull() ? false : true; +} + +MacAddress NDPNeighborAdvertisementLayer::getTargetMac() const +{ + NdpOption option = this->getNdpOption(NDPNeighborOptionTypes::NDP_OPTION_TARGET_LINK_LAYER); + + if (option.isNull()) + { + return MacAddress::Zero; + } + + return MacAddress(option.getValue()); +} + +std::string NDPNeighborAdvertisementLayer::toString() const +{ + std::ostringstream typeStream; + typeStream << "ICMPv6 Layer, Neighbor Advertisement Message, TargetIP: " << getTargetIP().toString(); + hasTargetMacInfo() ? typeStream << ", TargetMAC: " + getTargetMac().toString() : typeStream << ", no Option"; + + return typeStream.str(); +} + +} // namespace pcpp diff --git a/Packet++/src/NullLoopbackLayer.cpp b/Packet++/src/NullLoopbackLayer.cpp index bb0c6cf2aa..5f6b95b3e5 100644 --- a/Packet++/src/NullLoopbackLayer.cpp +++ b/Packet++/src/NullLoopbackLayer.cpp @@ -1,107 +1,107 @@ -#include "NullLoopbackLayer.h" -#include "EthLayer.h" -#include "IPv4Layer.h" -#include "IPv6Layer.h" -#include "PayloadLayer.h" -#include - -namespace pcpp -{ - -#define BSWAP16(x) (((x) >> 8) | ((x) << 8)) -#define BSWAP32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) \ - | (((x) & 0x0000FF00) << 8) | ((x) << 24)) - -#define IEEE_802_3_MAX_LEN 0x5dc - -NullLoopbackLayer::NullLoopbackLayer(uint32_t family) -{ - const size_t dataLen = sizeof(uint32_t); - m_DataLen = dataLen; - m_Data = new uint8_t[dataLen]; - memset(m_Data, 0, dataLen); - m_Protocol = NULL_LOOPBACK; - - setFamily(family); -} - -uint32_t NullLoopbackLayer::getFamily() const -{ - uint32_t family = *(uint32_t*)m_Data; - if ((family & 0xFFFF0000) != 0) - { - if ((family & 0xFF000000) == 0 && (family & 0x00FF0000) < 0x00060000) - { - family >>= 16; - } - else - { - family = BSWAP32(family); - } - } - else if ((family & 0x000000FF) == 0 && (family & 0x0000FF00) < 0x00000600) - { - family = BSWAP16(family & 0xFFFF); - } - - return family; -} - -void NullLoopbackLayer::setFamily(uint32_t family) -{ - *m_Data = family; -} - -void NullLoopbackLayer::parseNextLayer() -{ - uint8_t* payload = m_Data + sizeof(uint32_t); - size_t payloadLen = m_DataLen - sizeof(uint32_t); - - uint32_t family = getFamily(); - if (family > IEEE_802_3_MAX_LEN) - { - uint16_t ethType = (uint16_t)family; - switch (ethType) - { - case PCPP_ETHERTYPE_IP: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - return; - case PCPP_ETHERTYPE_IPV6: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - return; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - return; - } - } - - switch (family) - { - case PCPP_BSD_AF_INET: - m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - case PCPP_BSD_AF_INET6_BSD: - case PCPP_BSD_AF_INET6_FREEBSD: - case PCPP_BSD_AF_INET6_DARWIN: - m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) - ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) - : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); - break; - default: - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } -} - - -std::string NullLoopbackLayer::toString() const -{ - return "Null/Loopback"; -} - -} +#include "NullLoopbackLayer.h" +#include "EthLayer.h" +#include "IPv4Layer.h" +#include "IPv6Layer.h" +#include "PayloadLayer.h" +#include + +namespace pcpp +{ + +#define BSWAP16(x) (((x) >> 8) | ((x) << 8)) +#define BSWAP32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) \ + | (((x) & 0x0000FF00) << 8) | ((x) << 24)) + +#define IEEE_802_3_MAX_LEN 0x5dc + +NullLoopbackLayer::NullLoopbackLayer(uint32_t family) +{ + const size_t dataLen = sizeof(uint32_t); + m_DataLen = dataLen; + m_Data = new uint8_t[dataLen]; + memset(m_Data, 0, dataLen); + m_Protocol = NULL_LOOPBACK; + + setFamily(family); +} + +uint32_t NullLoopbackLayer::getFamily() const +{ + uint32_t family = *(uint32_t*)m_Data; + if ((family & 0xFFFF0000) != 0) + { + if ((family & 0xFF000000) == 0 && (family & 0x00FF0000) < 0x00060000) + { + family >>= 16; + } + else + { + family = BSWAP32(family); + } + } + else if ((family & 0x000000FF) == 0 && (family & 0x0000FF00) < 0x00000600) + { + family = BSWAP16(family & 0xFFFF); + } + + return family; +} + +void NullLoopbackLayer::setFamily(uint32_t family) +{ + *m_Data = family; +} + +void NullLoopbackLayer::parseNextLayer() +{ + uint8_t* payload = m_Data + sizeof(uint32_t); + size_t payloadLen = m_DataLen - sizeof(uint32_t); + + uint32_t family = getFamily(); + if (family > IEEE_802_3_MAX_LEN) + { + uint16_t ethType = (uint16_t)family; + switch (ethType) + { + case PCPP_ETHERTYPE_IP: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); + return; + case PCPP_ETHERTYPE_IPV6: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); + return; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + return; + } + } + + switch (family) + { + case PCPP_BSD_AF_INET: + m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen) + ? static_cast(new IPv4Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + case PCPP_BSD_AF_INET6_BSD: + case PCPP_BSD_AF_INET6_FREEBSD: + case PCPP_BSD_AF_INET6_DARWIN: + m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen) + ? static_cast(new IPv6Layer(payload, payloadLen, this, m_Packet)) + : static_cast(new PayloadLayer(payload, payloadLen, this, m_Packet)); + break; + default: + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } +} + + +std::string NullLoopbackLayer::toString() const +{ + return "Null/Loopback"; +} + +} diff --git a/Packet++/src/Packet.cpp b/Packet++/src/Packet.cpp index 959fb36234..57fd40404f 100644 --- a/Packet++/src/Packet.cpp +++ b/Packet++/src/Packet.cpp @@ -1,803 +1,803 @@ -#define LOG_MODULE PacketLogModulePacket - -#include "Packet.h" -#include "EthLayer.h" -#include "EthDot3Layer.h" -#include "SllLayer.h" -#include "NullLoopbackLayer.h" -#include "IPv4Layer.h" -#include "IPv6Layer.h" -#include "PayloadLayer.h" -#include "PacketTrailerLayer.h" -#include "Logger.h" -#include "EndianPortable.h" -#include -#include -#include -#ifdef _MSC_VER -#include -#include "SystemUtils.h" -#endif - - -namespace pcpp -{ - -Packet::Packet(size_t maxPacketLen) : - m_RawPacket(nullptr), - m_FirstLayer(nullptr), - m_LastLayer(nullptr), - m_ProtocolTypes(UnknownProtocol), - m_MaxPacketLen(maxPacketLen), - m_FreeRawPacket(true), - m_CanReallocateData(true) -{ - timeval time; - gettimeofday(&time, nullptr); - uint8_t* data = new uint8_t[maxPacketLen]; - memset(data, 0, maxPacketLen); - m_RawPacket = new RawPacket(data, 0, time, true, LINKTYPE_ETHERNET); -} - -Packet::Packet(uint8_t* buffer, size_t bufferSize) : - m_RawPacket(nullptr), - m_FirstLayer(nullptr), - m_LastLayer(nullptr), - m_ProtocolTypes(UnknownProtocol), - m_MaxPacketLen(bufferSize), - m_FreeRawPacket(true), - m_CanReallocateData(false) -{ - timeval time; - gettimeofday(&time, nullptr); - memset(buffer, 0, bufferSize); - m_RawPacket = new RawPacket(buffer, 0, time, false, LINKTYPE_ETHERNET); -} - -void Packet::setRawPacket(RawPacket* rawPacket, bool freeRawPacket, ProtocolType parseUntil, OsiModelLayer parseUntilLayer) -{ - destructPacketData(); - - m_FirstLayer = nullptr; - m_LastLayer = nullptr; - m_ProtocolTypes = UnknownProtocol; - m_MaxPacketLen = rawPacket->getRawDataLen(); - m_FreeRawPacket = freeRawPacket; - m_RawPacket = rawPacket; - m_CanReallocateData = true; - if (m_RawPacket == nullptr) - return; - - LinkLayerType linkType = m_RawPacket->getLinkLayerType(); - - m_FirstLayer = createFirstLayer(linkType); - - m_LastLayer = m_FirstLayer; - Layer* curLayer = m_FirstLayer; - while (curLayer != nullptr && (curLayer->getProtocol() & parseUntil) == 0 && curLayer->getOsiModelLayer() <= parseUntilLayer) - { - m_ProtocolTypes |= curLayer->getProtocol(); - curLayer->parseNextLayer(); - curLayer->m_IsAllocatedInPacket = true; - curLayer = curLayer->getNextLayer(); - if (curLayer != nullptr) - m_LastLayer = curLayer; - } - - if (curLayer != nullptr && (curLayer->getProtocol() & parseUntil) != 0) - { - m_ProtocolTypes |= curLayer->getProtocol(); - curLayer->m_IsAllocatedInPacket = true; - } - - if (curLayer != nullptr && curLayer->getOsiModelLayer() > parseUntilLayer) - { - m_LastLayer = curLayer->getPrevLayer(); - delete curLayer; - m_LastLayer->m_NextLayer = nullptr; - } - - if (m_LastLayer != nullptr && parseUntil == UnknownProtocol && parseUntilLayer == OsiModelLayerUnknown) - { - // find if there is data left in the raw packet that doesn't belong to any layer. In that case it's probably a packet trailer. - // create a PacketTrailerLayer layer and add it at the end of the packet - int trailerLen = (int)((m_RawPacket->getRawData() + m_RawPacket->getRawDataLen()) - (m_LastLayer->getData() + m_LastLayer->getDataLen())); - if (trailerLen > 0) - { - PacketTrailerLayer* trailerLayer = new PacketTrailerLayer( - (uint8_t*)(m_LastLayer->getData() + m_LastLayer->getDataLen()), - trailerLen, - m_LastLayer, - this); - - trailerLayer->m_IsAllocatedInPacket = true; - m_LastLayer->setNextLayer(trailerLayer); - m_LastLayer = trailerLayer; - m_ProtocolTypes |= trailerLayer->getProtocol(); - } - } -} - -Packet::Packet(RawPacket* rawPacket, bool freeRawPacket, ProtocolType parseUntil, OsiModelLayer parseUntilLayer) -{ - m_FreeRawPacket = false; - m_RawPacket = nullptr; - m_FirstLayer = nullptr; - setRawPacket(rawPacket, freeRawPacket, parseUntil, parseUntilLayer); -} - -Packet::Packet(RawPacket* rawPacket, ProtocolType parseUntil) -{ - m_FreeRawPacket = false; - m_RawPacket = nullptr; - m_FirstLayer = nullptr; - setRawPacket(rawPacket, false, parseUntil, OsiModelLayerUnknown); -} - -Packet::Packet(RawPacket* rawPacket, OsiModelLayer parseUntilLayer) -{ - m_FreeRawPacket = false; - m_RawPacket = nullptr; - m_FirstLayer = nullptr; - setRawPacket(rawPacket, false, UnknownProtocol, parseUntilLayer); -} - -void Packet::destructPacketData() -{ - Layer* curLayer = m_FirstLayer; - while (curLayer != nullptr) - { - Layer* nextLayer = curLayer->getNextLayer(); - if (curLayer->m_IsAllocatedInPacket) - delete curLayer; - curLayer = nextLayer; - } - - if (m_RawPacket != nullptr && m_FreeRawPacket) - { - delete m_RawPacket; - } -} - -Packet& Packet::operator=(const Packet& other) -{ - destructPacketData(); - - copyDataFrom(other); - - return *this; -} - -void Packet::copyDataFrom(const Packet& other) -{ - m_RawPacket = new RawPacket(*(other.m_RawPacket)); - m_FreeRawPacket = true; - m_MaxPacketLen = other.m_MaxPacketLen; - m_ProtocolTypes = other.m_ProtocolTypes; - m_FirstLayer = createFirstLayer(m_RawPacket->getLinkLayerType()); - m_LastLayer = m_FirstLayer; - Layer* curLayer = m_FirstLayer; - while (curLayer != nullptr) - { - curLayer->parseNextLayer(); - curLayer->m_IsAllocatedInPacket = true; - curLayer = curLayer->getNextLayer(); - if (curLayer != nullptr) - m_LastLayer = curLayer; - } -} - -void Packet::reallocateRawData(size_t newSize) -{ - PCPP_LOG_DEBUG("Allocating packet to new size: " << newSize); - - // allocate a new array with size newSize - m_MaxPacketLen = newSize; - - // set the new array to RawPacket - if (!m_RawPacket->reallocateData(m_MaxPacketLen)) - { - PCPP_LOG_ERROR("Couldn't reallocate data of raw packet to " << m_MaxPacketLen << " bytes"); - return; - } - - // set all data pointers in layers to the new array address - const uint8_t* dataPtr = m_RawPacket->getRawData(); - - Layer* curLayer = m_FirstLayer; - while (curLayer != nullptr) - { - PCPP_LOG_DEBUG("Setting new data pointer to layer '" << typeid(curLayer).name() << "'"); - curLayer->m_Data = (uint8_t*)dataPtr; - dataPtr += curLayer->getHeaderLen(); - curLayer = curLayer->getNextLayer(); - } -} - -bool Packet::insertLayer(Layer* prevLayer, Layer* newLayer, bool ownInPacket) -{ - if (newLayer == nullptr) - { - PCPP_LOG_ERROR("Layer to add is NULL"); - return false; - } - - if (newLayer->isAllocatedToPacket()) - { - PCPP_LOG_ERROR("Layer is already allocated to another packet. Cannot use layer in more than one packet"); - return false; - } - - if (prevLayer != nullptr && prevLayer->getProtocol() == PacketTrailer) - { - PCPP_LOG_ERROR("Cannot insert layer after packet trailer"); - return false; - } - - size_t newLayerHeaderLen = newLayer->getHeaderLen(); - if (m_RawPacket->getRawDataLen() + newLayerHeaderLen > m_MaxPacketLen) - { - if (!m_CanReallocateData) - { - PCPP_LOG_ERROR("With the new layer the packet will exceed the size of the pre-allocated buffer: " << m_MaxPacketLen << " bytes"); - return false; - } - // reallocate to maximum value of: twice the max size of the packet or max size + new required length - if (m_RawPacket->getRawDataLen() + newLayerHeaderLen > m_MaxPacketLen*2) - reallocateRawData(m_RawPacket->getRawDataLen() + newLayerHeaderLen + m_MaxPacketLen); - else - reallocateRawData(m_MaxPacketLen*2); - } - - // insert layer data to raw packet - int indexToInsertData = 0; - if (prevLayer != nullptr) - indexToInsertData = prevLayer->m_Data + prevLayer->getHeaderLen() - m_RawPacket->getRawData(); - m_RawPacket->insertData(indexToInsertData, newLayer->m_Data, newLayerHeaderLen); - - //delete previous layer data - delete[] newLayer->m_Data; - - // add layer to layers linked list - if (prevLayer != nullptr) - { - newLayer->setNextLayer(prevLayer->getNextLayer()); - newLayer->setPrevLayer(prevLayer); - prevLayer->setNextLayer(newLayer); - } - else //prevLayer == NULL - { - newLayer->setNextLayer(m_FirstLayer); - if (m_FirstLayer != nullptr) - m_FirstLayer->setPrevLayer(newLayer); - m_FirstLayer = newLayer; - } - - if (newLayer->getNextLayer() == nullptr) - m_LastLayer = newLayer; - else - newLayer->getNextLayer()->setPrevLayer(newLayer); - - // assign layer with this packet only - newLayer->m_Packet = this; - - // Set flag to indicate if new layer is allocated to packet. - if(ownInPacket) - newLayer->m_IsAllocatedInPacket = true; - - // re-calculate all layers data ptr and data length - - // first, get ptr and data length of the raw packet - const uint8_t* dataPtr = m_RawPacket->getRawData(); - size_t dataLen = (size_t)m_RawPacket->getRawDataLen(); - - // if a packet trailer exists, get its length - size_t packetTrailerLen = 0; - if (m_LastLayer != nullptr && m_LastLayer->getProtocol() == PacketTrailer) - packetTrailerLen = m_LastLayer->getDataLen(); - - // go over all layers from the first layer to the last layer and set the data ptr and data length for each one - Layer* curLayer = m_FirstLayer; - while (curLayer != nullptr) - { - // set data ptr to layer - curLayer->m_Data = (uint8_t*)dataPtr; - - // there is an assumption here that the packet trailer, if exists, corresponds to the L2 (data link) layers. - // so if there is a packet trailer and this layer is L2 (data link), set its data length to contain the whole data, including the - // packet trailer. If this layer is L3-7, exclude the packet trailer from its data length - if (curLayer->getOsiModelLayer() == OsiModelDataLinkLayer) - curLayer->m_DataLen = dataLen; - else - curLayer->m_DataLen = dataLen - packetTrailerLen; - - // advance data ptr and data length - dataPtr += curLayer->getHeaderLen(); - dataLen -= curLayer->getHeaderLen(); - - // move to next layer - curLayer = curLayer->getNextLayer(); - } - - // add layer protocol to protocol collection - m_ProtocolTypes |= newLayer->getProtocol(); - return true; -} - -bool Packet::removeLayer(ProtocolType layerType, int index) -{ - Layer* layerToRemove = getLayerOfType(layerType, index); - - if (layerToRemove != nullptr) - { - return removeLayer(layerToRemove, true); - } - else - { - PCPP_LOG_ERROR("Layer of the requested type was not found in packet"); - return false; - } -} - -bool Packet::removeFirstLayer() -{ - Layer* firstLayer = getFirstLayer(); - if (firstLayer == nullptr) - { - PCPP_LOG_ERROR("Packet has no layers"); - return false; - } - - return removeLayer(firstLayer, true); -} - -bool Packet::removeLastLayer() -{ - Layer* lastLayer = getLastLayer(); - if (lastLayer == nullptr) - { - PCPP_LOG_ERROR("Packet has no layers"); - return false; - } - - return removeLayer(lastLayer, true); -} - -bool Packet::removeAllLayersAfter(Layer* layer) -{ - Layer* curLayer = layer->getNextLayer(); - while (curLayer != nullptr) - { - Layer* tempLayer = curLayer->getNextLayer(); - if (!removeLayer(curLayer, true)) - return false; - curLayer = tempLayer; - } - - return true; -} - -Layer* Packet::detachLayer(ProtocolType layerType, int index) -{ - Layer* layerToDetach = getLayerOfType(layerType, index); - - if (layerToDetach != nullptr) - { - if (removeLayer(layerToDetach, false)) - return layerToDetach; - else - return nullptr; - } - else - { - PCPP_LOG_ERROR("Layer of the requested type was not found in packet"); - return nullptr; - } -} - -bool Packet::removeLayer(Layer* layer, bool tryToDelete) -{ - if (layer == nullptr) - { - PCPP_LOG_ERROR("Layer is NULL"); - return false; - } - - // verify layer is allocated to a packet - if (!layer->isAllocatedToPacket()) - { - PCPP_LOG_ERROR("Layer isn't allocated to any packet"); - return false; - } - - // verify layer is allocated to *this* packet - Layer* curLayer = layer; - while (curLayer->m_PrevLayer != nullptr) - curLayer = curLayer->m_PrevLayer; - if (curLayer != m_FirstLayer) - { - PCPP_LOG_ERROR("Layer isn't allocated to this packet"); - return false; - } - - // before removing the layer's data, copy it so it can be later assigned as the removed layer's data - size_t headerLen = layer->getHeaderLen(); - size_t layerOldDataSize = headerLen; - uint8_t* layerOldData = new uint8_t[layerOldDataSize]; - memcpy(layerOldData, layer->m_Data, layerOldDataSize); - - // remove data from raw packet - size_t numOfBytesToRemove = headerLen; - int indexOfDataToRemove = layer->m_Data - m_RawPacket->getRawData(); - if (!m_RawPacket->removeData(indexOfDataToRemove, numOfBytesToRemove)) - { - PCPP_LOG_ERROR("Couldn't remove data from packet"); - delete [] layerOldData; - return false; - } - - // remove layer from layers linked list - if (layer->m_PrevLayer != nullptr) - layer->m_PrevLayer->setNextLayer(layer->m_NextLayer); - if (layer->m_NextLayer != nullptr) - layer->m_NextLayer->setPrevLayer(layer->m_PrevLayer); - - // take care of head and tail ptrs - if (m_FirstLayer == layer) - m_FirstLayer = layer->m_NextLayer; - if (m_LastLayer == layer) - m_LastLayer = layer->m_PrevLayer; - layer->setNextLayer(nullptr); - layer->setPrevLayer(nullptr); - - // get packet trailer len if exists - size_t packetTrailerLen = 0; - if (m_LastLayer != nullptr && m_LastLayer->getProtocol() == PacketTrailer) - packetTrailerLen = m_LastLayer->getDataLen(); - - // re-calculate all layers data ptr and data length - - // first, get ptr and data length of the raw packet - const uint8_t* dataPtr = m_RawPacket->getRawData(); - size_t dataLen = (size_t)m_RawPacket->getRawDataLen(); - - curLayer = m_FirstLayer; - - // a flag to be set if there is another layer in this packet with the same protocol - bool anotherLayerWithSameProtocolExists = false; - - // go over all layers from the first layer to the last layer and set the data ptr and data length for each one - while (curLayer != nullptr) - { - // set data ptr to layer - curLayer->m_Data = (uint8_t*)dataPtr; - - // there is an assumption here that the packet trailer, if exists, corresponds to the L2 (data link) layers. - // so if there is a packet trailer and this layer is L2 (data link), set its data length to contain the whole data, including the - // packet trailer. If this layer is L3-7, exclude the packet trailer from its data length - if (curLayer->getOsiModelLayer() == OsiModelDataLinkLayer) - curLayer->m_DataLen = dataLen; - else - curLayer->m_DataLen = dataLen - packetTrailerLen; - - // check if current layer's protocol is the same as removed layer protocol and set the flag accordingly - if (curLayer->getProtocol() == layer->getProtocol()) - anotherLayerWithSameProtocolExists = true; - - // advance data ptr and data length - dataPtr += curLayer->getHeaderLen(); - dataLen -= curLayer->getHeaderLen(); - - // move to next layer - curLayer = curLayer->getNextLayer(); - } - - // remove layer protocol from protocol list if necessary - if (!anotherLayerWithSameProtocolExists) - m_ProtocolTypes &= ~((uint64_t)layer->getProtocol()); - - // if layer was allocated by this packet and tryToDelete flag is set, delete it - if (tryToDelete && layer->m_IsAllocatedInPacket) - { - delete layer; - delete [] layerOldData; - } - // if layer was not allocated by this packet or the tryToDelete is not set, detach it from the packet so it can be reused - else - { - layer->m_Packet = nullptr; - layer->m_Data = layerOldData; - layer->m_DataLen = layerOldDataSize; - } - - return true; -} - -Layer* Packet::getLayerOfType(ProtocolType layerType, int index) const -{ - Layer* curLayer = getFirstLayer(); - int curIndex = 0; - while (curLayer != nullptr) - { - if (curLayer->getProtocol() == layerType) - { - if (curIndex < index) - curIndex++; - else - break; - } - curLayer = curLayer->getNextLayer(); - } - - return curLayer; -} - -bool Packet::extendLayer(Layer* layer, int offsetInLayer, size_t numOfBytesToExtend) -{ - if (layer == nullptr) - { - PCPP_LOG_ERROR("Layer is NULL"); - return false; - } - - // verify layer is allocated to this packet - if (!(layer->m_Packet == this)) - { - PCPP_LOG_ERROR("Layer isn't allocated to this packet"); - return false; - } - - if (m_RawPacket->getRawDataLen() + numOfBytesToExtend > m_MaxPacketLen) - { - if (!m_CanReallocateData) - { - PCPP_LOG_ERROR("With the layer extended size the packet will exceed the size of the pre-allocated buffer: " << m_MaxPacketLen << " bytes"); - return false; - } - // reallocate to maximum value of: twice the max size of the packet or max size + new required length - if (m_RawPacket->getRawDataLen() + numOfBytesToExtend > m_MaxPacketLen*2) - reallocateRawData(m_RawPacket->getRawDataLen() + numOfBytesToExtend + m_MaxPacketLen); - else - reallocateRawData(m_MaxPacketLen*2); - } - - // insert layer data to raw packet - int indexToInsertData = layer->m_Data + offsetInLayer - m_RawPacket->getRawData(); - // passing NULL to insertData will move the data by numOfBytesToExtend - // no new data has to be created for this insertion which saves at least little time - // this move operation occurs on already allocated memory, which is backed by the reallocation if's provided above - // if offsetInLayer == layer->getHeaderLen() insertData will not move any data but only increase the packet size by numOfBytesToExtend - m_RawPacket->insertData(indexToInsertData, nullptr, numOfBytesToExtend); - - // re-calculate all layers data ptr and data length - const uint8_t* dataPtr = m_RawPacket->getRawData(); - - // go over all layers from the first layer to the last layer and set the data ptr and data length for each layer - Layer* curLayer = m_FirstLayer; - bool passedExtendedLayer = false; - while (curLayer != nullptr) - { - // set the data ptr - curLayer->m_Data = (uint8_t*)dataPtr; - - // set a flag if arrived to the layer being extended - if (curLayer->getPrevLayer() == layer) - passedExtendedLayer = true; - - // change the data length only for layers who come before the extended layer. For layers who come after, data length isn't changed - if (!passedExtendedLayer) - curLayer->m_DataLen += numOfBytesToExtend; - - // assuming header length of the layer that requested to be extended hasn't been enlarged yet - size_t headerLen = curLayer->getHeaderLen() + (curLayer == layer ? numOfBytesToExtend : 0); - dataPtr += headerLen; - curLayer = curLayer->getNextLayer(); - } - - return true; -} - -bool Packet::shortenLayer(Layer* layer, int offsetInLayer, size_t numOfBytesToShorten) -{ - if (layer == nullptr) - { - PCPP_LOG_ERROR("Layer is NULL"); - return false; - } - - // verify layer is allocated to this packet - if (!(layer->m_Packet == this)) - { - PCPP_LOG_ERROR("Layer isn't allocated to this packet"); - return false; - } - - // remove data from raw packet - int indexOfDataToRemove = layer->m_Data + offsetInLayer - m_RawPacket->getRawData(); - if (!m_RawPacket->removeData(indexOfDataToRemove, numOfBytesToShorten)) - { - PCPP_LOG_ERROR("Couldn't remove data from packet"); - return false; - } - - // re-calculate all layers data ptr and data length - const uint8_t* dataPtr = m_RawPacket->getRawData(); - - // go over all layers from the first layer to the last layer and set the data ptr and data length for each layer - Layer* curLayer = m_FirstLayer; - bool passedExtendedLayer = false; - while (curLayer != nullptr) - { - // set the data ptr - curLayer->m_Data = (uint8_t*)dataPtr; - - // set a flag if arrived to the layer being shortened - if (curLayer->getPrevLayer() == layer) - passedExtendedLayer = true; - - // change the data length only for layers who come before the shortened layer. For layers who come after, data length isn't changed - if (!passedExtendedLayer) - curLayer->m_DataLen -= numOfBytesToShorten; - - // assuming header length of the layer that requested to be extended hasn't been enlarged yet - size_t headerLen = curLayer->getHeaderLen() - (curLayer == layer ? numOfBytesToShorten : 0); - dataPtr += headerLen; - curLayer = curLayer->getNextLayer(); - } - - return true; -} - -void Packet::computeCalculateFields() -{ - // calculated fields should be calculated from top layer to bottom layer - - Layer* curLayer = m_LastLayer; - while (curLayer != nullptr) - { - curLayer->computeCalculateFields(); - curLayer = curLayer->getPrevLayer(); - } -} - -std::string Packet::printPacketInfo(bool timeAsLocalTime) const -{ - std::ostringstream dataLenStream; - dataLenStream << m_RawPacket->getRawDataLen(); - - // convert raw packet timestamp to printable format - timespec timestamp = m_RawPacket->getPacketTimeStamp(); - time_t nowtime = timestamp.tv_sec; - struct tm *nowtm = nullptr; -#if __cplusplus > 199711L && !defined(_WIN32) - // localtime_r and gmtime_r are thread-safe versions of localtime and gmtime, - // but they're defined only in newer compilers (>= C++0x). - // on Windows localtime and gmtime are already thread-safe so there is not need - // to use localtime_r and gmtime_r - struct tm nowtm_r; - if (timeAsLocalTime) - nowtm = localtime_r(&nowtime, &nowtm_r); - else - nowtm = gmtime_r(&nowtime, &nowtm_r); - - if (nowtm != nullptr) - nowtm = &nowtm_r; -#else - // on Window compilers localtime and gmtime are already thread safe. - // in old compilers (< C++0x) gmtime_r and localtime_r were not defined so we have to fall back to localtime and gmtime - if (timeAsLocalTime) - nowtm = localtime(&nowtime); - else - nowtm = gmtime(&nowtime); -#endif - - char buf[128]; - if (nowtm != nullptr) - { - char tmbuf[64]; - strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", nowtm); - snprintf(buf, sizeof(buf), "%s.%09lu", tmbuf, (unsigned long)timestamp.tv_nsec); - } - else - snprintf(buf, sizeof(buf), "0000-00-00 00:00:00.000000000"); - - return "Packet length: " + dataLenStream.str() + " [Bytes], Arrival time: " + std::string(buf); -} - -Layer* Packet::createFirstLayer(LinkLayerType linkType) -{ - size_t rawDataLen = (size_t)m_RawPacket->getRawDataLen(); - if (rawDataLen == 0) - return nullptr; - - const uint8_t* rawData = m_RawPacket->getRawData(); - - if (linkType == LINKTYPE_ETHERNET) - { - if (EthLayer::isDataValid(rawData, rawDataLen)) - { - return new EthLayer((uint8_t*)rawData, rawDataLen, this); - } - else if (EthDot3Layer::isDataValid(rawData, rawDataLen)) - { - return new EthDot3Layer((uint8_t*)rawData, rawDataLen, this); - } - else - { - return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); - } - } - else if (linkType == LINKTYPE_LINUX_SLL) - { - return new SllLayer((uint8_t*)rawData, rawDataLen, this); - } - else if (linkType == LINKTYPE_NULL) - { - if (rawDataLen >= sizeof(uint32_t)) - return new NullLoopbackLayer((uint8_t*)rawData, rawDataLen, this); - else // rawDataLen is too small fir Null/Loopback - return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); - } - else if (linkType == LINKTYPE_RAW || linkType == LINKTYPE_DLT_RAW1 || linkType == LINKTYPE_DLT_RAW2) - { - uint8_t ipVer = rawData[0] & 0xf0; - if (ipVer == 0x40) - { - return IPv4Layer::isDataValid(rawData, rawDataLen) - ? static_cast(new IPv4Layer((uint8_t*)rawData, rawDataLen, nullptr, this)) - : static_cast(new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this)); - } - else if (ipVer == 0x60) - { - return IPv6Layer::isDataValid(rawData, rawDataLen) - ? static_cast(new IPv6Layer((uint8_t*)rawData, rawDataLen, nullptr, this)) - : static_cast(new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this)); - } - else - { - return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); - } - } - else if (linkType == LINKTYPE_IPV4) - { - return IPv4Layer::isDataValid(rawData, rawDataLen) - ? static_cast(new IPv4Layer((uint8_t*)rawData, rawDataLen, nullptr, this)) - : static_cast(new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this)); - } - else if (linkType == LINKTYPE_IPV6) - { - return IPv6Layer::isDataValid(rawData, rawDataLen) - ? static_cast(new IPv6Layer((uint8_t*)rawData, rawDataLen, nullptr, this)) - : static_cast(new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this)); - } - - // unknown link type - return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); -} - -std::string Packet::toString(bool timeAsLocalTime) const -{ - std::vector stringList; - std::string result; - toStringList(stringList, timeAsLocalTime); - for (std::vector::iterator iter = stringList.begin(); iter != stringList.end(); iter++) - { - result += *iter + '\n'; - } - - return result; -} - -void Packet::toStringList(std::vector& result, bool timeAsLocalTime) const -{ - result.clear(); - result.push_back(printPacketInfo(timeAsLocalTime)); - Layer* curLayer = m_FirstLayer; - while (curLayer != nullptr) - { - result.push_back(curLayer->toString()); - curLayer = curLayer->getNextLayer(); - } -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModulePacket + +#include "Packet.h" +#include "EthLayer.h" +#include "EthDot3Layer.h" +#include "SllLayer.h" +#include "NullLoopbackLayer.h" +#include "IPv4Layer.h" +#include "IPv6Layer.h" +#include "PayloadLayer.h" +#include "PacketTrailerLayer.h" +#include "Logger.h" +#include "EndianPortable.h" +#include +#include +#include +#ifdef _MSC_VER +#include +#include "SystemUtils.h" +#endif + + +namespace pcpp +{ + +Packet::Packet(size_t maxPacketLen) : + m_RawPacket(nullptr), + m_FirstLayer(nullptr), + m_LastLayer(nullptr), + m_ProtocolTypes(UnknownProtocol), + m_MaxPacketLen(maxPacketLen), + m_FreeRawPacket(true), + m_CanReallocateData(true) +{ + timeval time; + gettimeofday(&time, nullptr); + uint8_t* data = new uint8_t[maxPacketLen]; + memset(data, 0, maxPacketLen); + m_RawPacket = new RawPacket(data, 0, time, true, LINKTYPE_ETHERNET); +} + +Packet::Packet(uint8_t* buffer, size_t bufferSize) : + m_RawPacket(nullptr), + m_FirstLayer(nullptr), + m_LastLayer(nullptr), + m_ProtocolTypes(UnknownProtocol), + m_MaxPacketLen(bufferSize), + m_FreeRawPacket(true), + m_CanReallocateData(false) +{ + timeval time; + gettimeofday(&time, nullptr); + memset(buffer, 0, bufferSize); + m_RawPacket = new RawPacket(buffer, 0, time, false, LINKTYPE_ETHERNET); +} + +void Packet::setRawPacket(RawPacket* rawPacket, bool freeRawPacket, ProtocolType parseUntil, OsiModelLayer parseUntilLayer) +{ + destructPacketData(); + + m_FirstLayer = nullptr; + m_LastLayer = nullptr; + m_ProtocolTypes = UnknownProtocol; + m_MaxPacketLen = rawPacket->getRawDataLen(); + m_FreeRawPacket = freeRawPacket; + m_RawPacket = rawPacket; + m_CanReallocateData = true; + if (m_RawPacket == nullptr) + return; + + LinkLayerType linkType = m_RawPacket->getLinkLayerType(); + + m_FirstLayer = createFirstLayer(linkType); + + m_LastLayer = m_FirstLayer; + Layer* curLayer = m_FirstLayer; + while (curLayer != nullptr && (curLayer->getProtocol() & parseUntil) == 0 && curLayer->getOsiModelLayer() <= parseUntilLayer) + { + m_ProtocolTypes |= curLayer->getProtocol(); + curLayer->parseNextLayer(); + curLayer->m_IsAllocatedInPacket = true; + curLayer = curLayer->getNextLayer(); + if (curLayer != nullptr) + m_LastLayer = curLayer; + } + + if (curLayer != nullptr && (curLayer->getProtocol() & parseUntil) != 0) + { + m_ProtocolTypes |= curLayer->getProtocol(); + curLayer->m_IsAllocatedInPacket = true; + } + + if (curLayer != nullptr && curLayer->getOsiModelLayer() > parseUntilLayer) + { + m_LastLayer = curLayer->getPrevLayer(); + delete curLayer; + m_LastLayer->m_NextLayer = nullptr; + } + + if (m_LastLayer != nullptr && parseUntil == UnknownProtocol && parseUntilLayer == OsiModelLayerUnknown) + { + // find if there is data left in the raw packet that doesn't belong to any layer. In that case it's probably a packet trailer. + // create a PacketTrailerLayer layer and add it at the end of the packet + int trailerLen = (int)((m_RawPacket->getRawData() + m_RawPacket->getRawDataLen()) - (m_LastLayer->getData() + m_LastLayer->getDataLen())); + if (trailerLen > 0) + { + PacketTrailerLayer* trailerLayer = new PacketTrailerLayer( + (uint8_t*)(m_LastLayer->getData() + m_LastLayer->getDataLen()), + trailerLen, + m_LastLayer, + this); + + trailerLayer->m_IsAllocatedInPacket = true; + m_LastLayer->setNextLayer(trailerLayer); + m_LastLayer = trailerLayer; + m_ProtocolTypes |= trailerLayer->getProtocol(); + } + } +} + +Packet::Packet(RawPacket* rawPacket, bool freeRawPacket, ProtocolType parseUntil, OsiModelLayer parseUntilLayer) +{ + m_FreeRawPacket = false; + m_RawPacket = nullptr; + m_FirstLayer = nullptr; + setRawPacket(rawPacket, freeRawPacket, parseUntil, parseUntilLayer); +} + +Packet::Packet(RawPacket* rawPacket, ProtocolType parseUntil) +{ + m_FreeRawPacket = false; + m_RawPacket = nullptr; + m_FirstLayer = nullptr; + setRawPacket(rawPacket, false, parseUntil, OsiModelLayerUnknown); +} + +Packet::Packet(RawPacket* rawPacket, OsiModelLayer parseUntilLayer) +{ + m_FreeRawPacket = false; + m_RawPacket = nullptr; + m_FirstLayer = nullptr; + setRawPacket(rawPacket, false, UnknownProtocol, parseUntilLayer); +} + +void Packet::destructPacketData() +{ + Layer* curLayer = m_FirstLayer; + while (curLayer != nullptr) + { + Layer* nextLayer = curLayer->getNextLayer(); + if (curLayer->m_IsAllocatedInPacket) + delete curLayer; + curLayer = nextLayer; + } + + if (m_RawPacket != nullptr && m_FreeRawPacket) + { + delete m_RawPacket; + } +} + +Packet& Packet::operator=(const Packet& other) +{ + destructPacketData(); + + copyDataFrom(other); + + return *this; +} + +void Packet::copyDataFrom(const Packet& other) +{ + m_RawPacket = new RawPacket(*(other.m_RawPacket)); + m_FreeRawPacket = true; + m_MaxPacketLen = other.m_MaxPacketLen; + m_ProtocolTypes = other.m_ProtocolTypes; + m_FirstLayer = createFirstLayer(m_RawPacket->getLinkLayerType()); + m_LastLayer = m_FirstLayer; + Layer* curLayer = m_FirstLayer; + while (curLayer != nullptr) + { + curLayer->parseNextLayer(); + curLayer->m_IsAllocatedInPacket = true; + curLayer = curLayer->getNextLayer(); + if (curLayer != nullptr) + m_LastLayer = curLayer; + } +} + +void Packet::reallocateRawData(size_t newSize) +{ + PCPP_LOG_DEBUG("Allocating packet to new size: " << newSize); + + // allocate a new array with size newSize + m_MaxPacketLen = newSize; + + // set the new array to RawPacket + if (!m_RawPacket->reallocateData(m_MaxPacketLen)) + { + PCPP_LOG_ERROR("Couldn't reallocate data of raw packet to " << m_MaxPacketLen << " bytes"); + return; + } + + // set all data pointers in layers to the new array address + const uint8_t* dataPtr = m_RawPacket->getRawData(); + + Layer* curLayer = m_FirstLayer; + while (curLayer != nullptr) + { + PCPP_LOG_DEBUG("Setting new data pointer to layer '" << typeid(curLayer).name() << "'"); + curLayer->m_Data = (uint8_t*)dataPtr; + dataPtr += curLayer->getHeaderLen(); + curLayer = curLayer->getNextLayer(); + } +} + +bool Packet::insertLayer(Layer* prevLayer, Layer* newLayer, bool ownInPacket) +{ + if (newLayer == nullptr) + { + PCPP_LOG_ERROR("Layer to add is NULL"); + return false; + } + + if (newLayer->isAllocatedToPacket()) + { + PCPP_LOG_ERROR("Layer is already allocated to another packet. Cannot use layer in more than one packet"); + return false; + } + + if (prevLayer != nullptr && prevLayer->getProtocol() == PacketTrailer) + { + PCPP_LOG_ERROR("Cannot insert layer after packet trailer"); + return false; + } + + size_t newLayerHeaderLen = newLayer->getHeaderLen(); + if (m_RawPacket->getRawDataLen() + newLayerHeaderLen > m_MaxPacketLen) + { + if (!m_CanReallocateData) + { + PCPP_LOG_ERROR("With the new layer the packet will exceed the size of the pre-allocated buffer: " << m_MaxPacketLen << " bytes"); + return false; + } + // reallocate to maximum value of: twice the max size of the packet or max size + new required length + if (m_RawPacket->getRawDataLen() + newLayerHeaderLen > m_MaxPacketLen*2) + reallocateRawData(m_RawPacket->getRawDataLen() + newLayerHeaderLen + m_MaxPacketLen); + else + reallocateRawData(m_MaxPacketLen*2); + } + + // insert layer data to raw packet + int indexToInsertData = 0; + if (prevLayer != nullptr) + indexToInsertData = prevLayer->m_Data + prevLayer->getHeaderLen() - m_RawPacket->getRawData(); + m_RawPacket->insertData(indexToInsertData, newLayer->m_Data, newLayerHeaderLen); + + //delete previous layer data + delete[] newLayer->m_Data; + + // add layer to layers linked list + if (prevLayer != nullptr) + { + newLayer->setNextLayer(prevLayer->getNextLayer()); + newLayer->setPrevLayer(prevLayer); + prevLayer->setNextLayer(newLayer); + } + else //prevLayer == NULL + { + newLayer->setNextLayer(m_FirstLayer); + if (m_FirstLayer != nullptr) + m_FirstLayer->setPrevLayer(newLayer); + m_FirstLayer = newLayer; + } + + if (newLayer->getNextLayer() == nullptr) + m_LastLayer = newLayer; + else + newLayer->getNextLayer()->setPrevLayer(newLayer); + + // assign layer with this packet only + newLayer->m_Packet = this; + + // Set flag to indicate if new layer is allocated to packet. + if(ownInPacket) + newLayer->m_IsAllocatedInPacket = true; + + // re-calculate all layers data ptr and data length + + // first, get ptr and data length of the raw packet + const uint8_t* dataPtr = m_RawPacket->getRawData(); + size_t dataLen = (size_t)m_RawPacket->getRawDataLen(); + + // if a packet trailer exists, get its length + size_t packetTrailerLen = 0; + if (m_LastLayer != nullptr && m_LastLayer->getProtocol() == PacketTrailer) + packetTrailerLen = m_LastLayer->getDataLen(); + + // go over all layers from the first layer to the last layer and set the data ptr and data length for each one + Layer* curLayer = m_FirstLayer; + while (curLayer != nullptr) + { + // set data ptr to layer + curLayer->m_Data = (uint8_t*)dataPtr; + + // there is an assumption here that the packet trailer, if exists, corresponds to the L2 (data link) layers. + // so if there is a packet trailer and this layer is L2 (data link), set its data length to contain the whole data, including the + // packet trailer. If this layer is L3-7, exclude the packet trailer from its data length + if (curLayer->getOsiModelLayer() == OsiModelDataLinkLayer) + curLayer->m_DataLen = dataLen; + else + curLayer->m_DataLen = dataLen - packetTrailerLen; + + // advance data ptr and data length + dataPtr += curLayer->getHeaderLen(); + dataLen -= curLayer->getHeaderLen(); + + // move to next layer + curLayer = curLayer->getNextLayer(); + } + + // add layer protocol to protocol collection + m_ProtocolTypes |= newLayer->getProtocol(); + return true; +} + +bool Packet::removeLayer(ProtocolType layerType, int index) +{ + Layer* layerToRemove = getLayerOfType(layerType, index); + + if (layerToRemove != nullptr) + { + return removeLayer(layerToRemove, true); + } + else + { + PCPP_LOG_ERROR("Layer of the requested type was not found in packet"); + return false; + } +} + +bool Packet::removeFirstLayer() +{ + Layer* firstLayer = getFirstLayer(); + if (firstLayer == nullptr) + { + PCPP_LOG_ERROR("Packet has no layers"); + return false; + } + + return removeLayer(firstLayer, true); +} + +bool Packet::removeLastLayer() +{ + Layer* lastLayer = getLastLayer(); + if (lastLayer == nullptr) + { + PCPP_LOG_ERROR("Packet has no layers"); + return false; + } + + return removeLayer(lastLayer, true); +} + +bool Packet::removeAllLayersAfter(Layer* layer) +{ + Layer* curLayer = layer->getNextLayer(); + while (curLayer != nullptr) + { + Layer* tempLayer = curLayer->getNextLayer(); + if (!removeLayer(curLayer, true)) + return false; + curLayer = tempLayer; + } + + return true; +} + +Layer* Packet::detachLayer(ProtocolType layerType, int index) +{ + Layer* layerToDetach = getLayerOfType(layerType, index); + + if (layerToDetach != nullptr) + { + if (removeLayer(layerToDetach, false)) + return layerToDetach; + else + return nullptr; + } + else + { + PCPP_LOG_ERROR("Layer of the requested type was not found in packet"); + return nullptr; + } +} + +bool Packet::removeLayer(Layer* layer, bool tryToDelete) +{ + if (layer == nullptr) + { + PCPP_LOG_ERROR("Layer is NULL"); + return false; + } + + // verify layer is allocated to a packet + if (!layer->isAllocatedToPacket()) + { + PCPP_LOG_ERROR("Layer isn't allocated to any packet"); + return false; + } + + // verify layer is allocated to *this* packet + Layer* curLayer = layer; + while (curLayer->m_PrevLayer != nullptr) + curLayer = curLayer->m_PrevLayer; + if (curLayer != m_FirstLayer) + { + PCPP_LOG_ERROR("Layer isn't allocated to this packet"); + return false; + } + + // before removing the layer's data, copy it so it can be later assigned as the removed layer's data + size_t headerLen = layer->getHeaderLen(); + size_t layerOldDataSize = headerLen; + uint8_t* layerOldData = new uint8_t[layerOldDataSize]; + memcpy(layerOldData, layer->m_Data, layerOldDataSize); + + // remove data from raw packet + size_t numOfBytesToRemove = headerLen; + int indexOfDataToRemove = layer->m_Data - m_RawPacket->getRawData(); + if (!m_RawPacket->removeData(indexOfDataToRemove, numOfBytesToRemove)) + { + PCPP_LOG_ERROR("Couldn't remove data from packet"); + delete [] layerOldData; + return false; + } + + // remove layer from layers linked list + if (layer->m_PrevLayer != nullptr) + layer->m_PrevLayer->setNextLayer(layer->m_NextLayer); + if (layer->m_NextLayer != nullptr) + layer->m_NextLayer->setPrevLayer(layer->m_PrevLayer); + + // take care of head and tail ptrs + if (m_FirstLayer == layer) + m_FirstLayer = layer->m_NextLayer; + if (m_LastLayer == layer) + m_LastLayer = layer->m_PrevLayer; + layer->setNextLayer(nullptr); + layer->setPrevLayer(nullptr); + + // get packet trailer len if exists + size_t packetTrailerLen = 0; + if (m_LastLayer != nullptr && m_LastLayer->getProtocol() == PacketTrailer) + packetTrailerLen = m_LastLayer->getDataLen(); + + // re-calculate all layers data ptr and data length + + // first, get ptr and data length of the raw packet + const uint8_t* dataPtr = m_RawPacket->getRawData(); + size_t dataLen = (size_t)m_RawPacket->getRawDataLen(); + + curLayer = m_FirstLayer; + + // a flag to be set if there is another layer in this packet with the same protocol + bool anotherLayerWithSameProtocolExists = false; + + // go over all layers from the first layer to the last layer and set the data ptr and data length for each one + while (curLayer != nullptr) + { + // set data ptr to layer + curLayer->m_Data = (uint8_t*)dataPtr; + + // there is an assumption here that the packet trailer, if exists, corresponds to the L2 (data link) layers. + // so if there is a packet trailer and this layer is L2 (data link), set its data length to contain the whole data, including the + // packet trailer. If this layer is L3-7, exclude the packet trailer from its data length + if (curLayer->getOsiModelLayer() == OsiModelDataLinkLayer) + curLayer->m_DataLen = dataLen; + else + curLayer->m_DataLen = dataLen - packetTrailerLen; + + // check if current layer's protocol is the same as removed layer protocol and set the flag accordingly + if (curLayer->getProtocol() == layer->getProtocol()) + anotherLayerWithSameProtocolExists = true; + + // advance data ptr and data length + dataPtr += curLayer->getHeaderLen(); + dataLen -= curLayer->getHeaderLen(); + + // move to next layer + curLayer = curLayer->getNextLayer(); + } + + // remove layer protocol from protocol list if necessary + if (!anotherLayerWithSameProtocolExists) + m_ProtocolTypes &= ~((uint64_t)layer->getProtocol()); + + // if layer was allocated by this packet and tryToDelete flag is set, delete it + if (tryToDelete && layer->m_IsAllocatedInPacket) + { + delete layer; + delete [] layerOldData; + } + // if layer was not allocated by this packet or the tryToDelete is not set, detach it from the packet so it can be reused + else + { + layer->m_Packet = nullptr; + layer->m_Data = layerOldData; + layer->m_DataLen = layerOldDataSize; + } + + return true; +} + +Layer* Packet::getLayerOfType(ProtocolType layerType, int index) const +{ + Layer* curLayer = getFirstLayer(); + int curIndex = 0; + while (curLayer != nullptr) + { + if (curLayer->getProtocol() == layerType) + { + if (curIndex < index) + curIndex++; + else + break; + } + curLayer = curLayer->getNextLayer(); + } + + return curLayer; +} + +bool Packet::extendLayer(Layer* layer, int offsetInLayer, size_t numOfBytesToExtend) +{ + if (layer == nullptr) + { + PCPP_LOG_ERROR("Layer is NULL"); + return false; + } + + // verify layer is allocated to this packet + if (!(layer->m_Packet == this)) + { + PCPP_LOG_ERROR("Layer isn't allocated to this packet"); + return false; + } + + if (m_RawPacket->getRawDataLen() + numOfBytesToExtend > m_MaxPacketLen) + { + if (!m_CanReallocateData) + { + PCPP_LOG_ERROR("With the layer extended size the packet will exceed the size of the pre-allocated buffer: " << m_MaxPacketLen << " bytes"); + return false; + } + // reallocate to maximum value of: twice the max size of the packet or max size + new required length + if (m_RawPacket->getRawDataLen() + numOfBytesToExtend > m_MaxPacketLen*2) + reallocateRawData(m_RawPacket->getRawDataLen() + numOfBytesToExtend + m_MaxPacketLen); + else + reallocateRawData(m_MaxPacketLen*2); + } + + // insert layer data to raw packet + int indexToInsertData = layer->m_Data + offsetInLayer - m_RawPacket->getRawData(); + // passing NULL to insertData will move the data by numOfBytesToExtend + // no new data has to be created for this insertion which saves at least little time + // this move operation occurs on already allocated memory, which is backed by the reallocation if's provided above + // if offsetInLayer == layer->getHeaderLen() insertData will not move any data but only increase the packet size by numOfBytesToExtend + m_RawPacket->insertData(indexToInsertData, nullptr, numOfBytesToExtend); + + // re-calculate all layers data ptr and data length + const uint8_t* dataPtr = m_RawPacket->getRawData(); + + // go over all layers from the first layer to the last layer and set the data ptr and data length for each layer + Layer* curLayer = m_FirstLayer; + bool passedExtendedLayer = false; + while (curLayer != nullptr) + { + // set the data ptr + curLayer->m_Data = (uint8_t*)dataPtr; + + // set a flag if arrived to the layer being extended + if (curLayer->getPrevLayer() == layer) + passedExtendedLayer = true; + + // change the data length only for layers who come before the extended layer. For layers who come after, data length isn't changed + if (!passedExtendedLayer) + curLayer->m_DataLen += numOfBytesToExtend; + + // assuming header length of the layer that requested to be extended hasn't been enlarged yet + size_t headerLen = curLayer->getHeaderLen() + (curLayer == layer ? numOfBytesToExtend : 0); + dataPtr += headerLen; + curLayer = curLayer->getNextLayer(); + } + + return true; +} + +bool Packet::shortenLayer(Layer* layer, int offsetInLayer, size_t numOfBytesToShorten) +{ + if (layer == nullptr) + { + PCPP_LOG_ERROR("Layer is NULL"); + return false; + } + + // verify layer is allocated to this packet + if (!(layer->m_Packet == this)) + { + PCPP_LOG_ERROR("Layer isn't allocated to this packet"); + return false; + } + + // remove data from raw packet + int indexOfDataToRemove = layer->m_Data + offsetInLayer - m_RawPacket->getRawData(); + if (!m_RawPacket->removeData(indexOfDataToRemove, numOfBytesToShorten)) + { + PCPP_LOG_ERROR("Couldn't remove data from packet"); + return false; + } + + // re-calculate all layers data ptr and data length + const uint8_t* dataPtr = m_RawPacket->getRawData(); + + // go over all layers from the first layer to the last layer and set the data ptr and data length for each layer + Layer* curLayer = m_FirstLayer; + bool passedExtendedLayer = false; + while (curLayer != nullptr) + { + // set the data ptr + curLayer->m_Data = (uint8_t*)dataPtr; + + // set a flag if arrived to the layer being shortened + if (curLayer->getPrevLayer() == layer) + passedExtendedLayer = true; + + // change the data length only for layers who come before the shortened layer. For layers who come after, data length isn't changed + if (!passedExtendedLayer) + curLayer->m_DataLen -= numOfBytesToShorten; + + // assuming header length of the layer that requested to be extended hasn't been enlarged yet + size_t headerLen = curLayer->getHeaderLen() - (curLayer == layer ? numOfBytesToShorten : 0); + dataPtr += headerLen; + curLayer = curLayer->getNextLayer(); + } + + return true; +} + +void Packet::computeCalculateFields() +{ + // calculated fields should be calculated from top layer to bottom layer + + Layer* curLayer = m_LastLayer; + while (curLayer != nullptr) + { + curLayer->computeCalculateFields(); + curLayer = curLayer->getPrevLayer(); + } +} + +std::string Packet::printPacketInfo(bool timeAsLocalTime) const +{ + std::ostringstream dataLenStream; + dataLenStream << m_RawPacket->getRawDataLen(); + + // convert raw packet timestamp to printable format + timespec timestamp = m_RawPacket->getPacketTimeStamp(); + time_t nowtime = timestamp.tv_sec; + struct tm *nowtm = nullptr; +#if __cplusplus > 199711L && !defined(_WIN32) + // localtime_r and gmtime_r are thread-safe versions of localtime and gmtime, + // but they're defined only in newer compilers (>= C++0x). + // on Windows localtime and gmtime are already thread-safe so there is not need + // to use localtime_r and gmtime_r + struct tm nowtm_r; + if (timeAsLocalTime) + nowtm = localtime_r(&nowtime, &nowtm_r); + else + nowtm = gmtime_r(&nowtime, &nowtm_r); + + if (nowtm != nullptr) + nowtm = &nowtm_r; +#else + // on Window compilers localtime and gmtime are already thread safe. + // in old compilers (< C++0x) gmtime_r and localtime_r were not defined so we have to fall back to localtime and gmtime + if (timeAsLocalTime) + nowtm = localtime(&nowtime); + else + nowtm = gmtime(&nowtime); +#endif + + char buf[128]; + if (nowtm != nullptr) + { + char tmbuf[64]; + strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", nowtm); + snprintf(buf, sizeof(buf), "%s.%09lu", tmbuf, (unsigned long)timestamp.tv_nsec); + } + else + snprintf(buf, sizeof(buf), "0000-00-00 00:00:00.000000000"); + + return "Packet length: " + dataLenStream.str() + " [Bytes], Arrival time: " + std::string(buf); +} + +Layer* Packet::createFirstLayer(LinkLayerType linkType) +{ + size_t rawDataLen = (size_t)m_RawPacket->getRawDataLen(); + if (rawDataLen == 0) + return nullptr; + + const uint8_t* rawData = m_RawPacket->getRawData(); + + if (linkType == LINKTYPE_ETHERNET) + { + if (EthLayer::isDataValid(rawData, rawDataLen)) + { + return new EthLayer((uint8_t*)rawData, rawDataLen, this); + } + else if (EthDot3Layer::isDataValid(rawData, rawDataLen)) + { + return new EthDot3Layer((uint8_t*)rawData, rawDataLen, this); + } + else + { + return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); + } + } + else if (linkType == LINKTYPE_LINUX_SLL) + { + return new SllLayer((uint8_t*)rawData, rawDataLen, this); + } + else if (linkType == LINKTYPE_NULL) + { + if (rawDataLen >= sizeof(uint32_t)) + return new NullLoopbackLayer((uint8_t*)rawData, rawDataLen, this); + else // rawDataLen is too small fir Null/Loopback + return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); + } + else if (linkType == LINKTYPE_RAW || linkType == LINKTYPE_DLT_RAW1 || linkType == LINKTYPE_DLT_RAW2) + { + uint8_t ipVer = rawData[0] & 0xf0; + if (ipVer == 0x40) + { + return IPv4Layer::isDataValid(rawData, rawDataLen) + ? static_cast(new IPv4Layer((uint8_t*)rawData, rawDataLen, nullptr, this)) + : static_cast(new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this)); + } + else if (ipVer == 0x60) + { + return IPv6Layer::isDataValid(rawData, rawDataLen) + ? static_cast(new IPv6Layer((uint8_t*)rawData, rawDataLen, nullptr, this)) + : static_cast(new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this)); + } + else + { + return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); + } + } + else if (linkType == LINKTYPE_IPV4) + { + return IPv4Layer::isDataValid(rawData, rawDataLen) + ? static_cast(new IPv4Layer((uint8_t*)rawData, rawDataLen, nullptr, this)) + : static_cast(new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this)); + } + else if (linkType == LINKTYPE_IPV6) + { + return IPv6Layer::isDataValid(rawData, rawDataLen) + ? static_cast(new IPv6Layer((uint8_t*)rawData, rawDataLen, nullptr, this)) + : static_cast(new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this)); + } + + // unknown link type + return new PayloadLayer((uint8_t*)rawData, rawDataLen, nullptr, this); +} + +std::string Packet::toString(bool timeAsLocalTime) const +{ + std::vector stringList; + std::string result; + toStringList(stringList, timeAsLocalTime); + for (std::vector::iterator iter = stringList.begin(); iter != stringList.end(); iter++) + { + result += *iter + '\n'; + } + + return result; +} + +void Packet::toStringList(std::vector& result, bool timeAsLocalTime) const +{ + result.clear(); + result.push_back(printPacketInfo(timeAsLocalTime)); + Layer* curLayer = m_FirstLayer; + while (curLayer != nullptr) + { + result.push_back(curLayer->toString()); + curLayer = curLayer->getNextLayer(); + } +} + +} // namespace pcpp diff --git a/Packet++/src/PacketTrailerLayer.cpp b/Packet++/src/PacketTrailerLayer.cpp index 211df610de..60377ffb04 100644 --- a/Packet++/src/PacketTrailerLayer.cpp +++ b/Packet++/src/PacketTrailerLayer.cpp @@ -1,27 +1,27 @@ -#include "PacketTrailerLayer.h" -#include "GeneralUtils.h" -#include -#include - -namespace pcpp -{ - -std::string PacketTrailerLayer::getTrailerDataAsHexString() const -{ - return byteArrayToHexString(m_Data, m_DataLen, m_DataLen + 4); -} - -std::string PacketTrailerLayer::toString() const -{ - std::ostringstream dataLenStream; - dataLenStream << m_DataLen; - - std::string trailerStr = byteArrayToHexString(m_Data, m_DataLen, 15); - - if (m_DataLen > 15) - trailerStr += "..."; - - return "Packet Trailer, Data: " + trailerStr + ", Length: " + dataLenStream.str() + " [Bytes]"; -} - -} +#include "PacketTrailerLayer.h" +#include "GeneralUtils.h" +#include +#include + +namespace pcpp +{ + +std::string PacketTrailerLayer::getTrailerDataAsHexString() const +{ + return byteArrayToHexString(m_Data, m_DataLen, m_DataLen + 4); +} + +std::string PacketTrailerLayer::toString() const +{ + std::ostringstream dataLenStream; + dataLenStream << m_DataLen; + + std::string trailerStr = byteArrayToHexString(m_Data, m_DataLen, 15); + + if (m_DataLen > 15) + trailerStr += "..."; + + return "Packet Trailer, Data: " + trailerStr + ", Length: " + dataLenStream.str() + " [Bytes]"; +} + +} diff --git a/Packet++/src/PacketUtils.cpp b/Packet++/src/PacketUtils.cpp index 7c9a414845..f5bbf86806 100644 --- a/Packet++/src/PacketUtils.cpp +++ b/Packet++/src/PacketUtils.cpp @@ -1,202 +1,202 @@ -#include - -#include "PacketUtils.h" -#include "IPv4Layer.h" -#include "IPv6Layer.h" -#include "TcpLayer.h" -#include "UdpLayer.h" -#include "IcmpLayer.h" -#include "Logger.h" -#include "EndianPortable.h" - -namespace pcpp -{ - -uint16_t computeChecksum(ScalarBuffer vec[], size_t vecSize) -{ - uint32_t sum = 0; - for (size_t i = 0; i>16) - { - localSum = (localSum & 0xffff) + (localSum >> 16); - } - PCPP_LOG_DEBUG("Local sum = " << localSum << ", 0x" << std::uppercase << std::hex << localSum); - sum += localSum; - } - - while (sum>>16) - { - sum = (sum & 0xffff) + (sum >> 16); - } - PCPP_LOG_DEBUG("Sum before invert = " << sum << ", 0x" << std::uppercase << std::hex << sum); - - // To obtain the checksum we take the ones' complement of this result - uint16_t result = sum; - result = ~result; - - PCPP_LOG_DEBUG("Calculated checksum = " << sum << ", 0x" << std::uppercase << std::hex << result); - - // We return the result in BigEndian byte order - return htobe16(result); -} - -static const uint32_t FNV_PRIME = 16777619u; -static const uint32_t OFFSET_BASIS = 2166136261u; - -uint32_t fnvHash(ScalarBuffer vec[], size_t vecSize) -{ - uint32_t hash = OFFSET_BASIS; - for (size_t i = 0; i < vecSize; ++i) - { - for (size_t j = 0; j < vec[i].len; ++j) - { - hash *= FNV_PRIME; - hash ^= vec[i].buffer[j]; - } - } - return hash; -} - -uint32_t fnvHash(uint8_t* buffer, size_t bufSize) -{ - ScalarBuffer scalarBuf; - scalarBuf.buffer = buffer; - scalarBuf.len = bufSize; - return fnvHash(&scalarBuf, 1); -} - -uint32_t hash5Tuple(Packet* packet, bool const& directionUnique) -{ - if (!packet->isPacketOfType(IPv4) && !packet->isPacketOfType(IPv6)) - return 0; - - if (packet->isPacketOfType(ICMP)) - return 0; - - if (!(packet->isPacketOfType(TCP)) && (!packet->isPacketOfType(UDP))) - return 0; - - ScalarBuffer vec[5]; - - uint16_t portSrc = 0; - uint16_t portDst = 0; - int srcPosition = 0; - - TcpLayer* tcpLayer = packet->getLayerOfType(true); // lookup in reverse order - if (tcpLayer != nullptr) - { - portSrc = tcpLayer->getTcpHeader()->portSrc; - portDst = tcpLayer->getTcpHeader()->portDst; - } - else - { - UdpLayer* udpLayer = packet->getLayerOfType(true); - portSrc = udpLayer->getUdpHeader()->portSrc; - portDst = udpLayer->getUdpHeader()->portDst; - } - - if( ! directionUnique) - { - if (portDst < portSrc) - srcPosition = 1; - } - - vec[0 + srcPosition].buffer = (uint8_t*)&portSrc; - vec[0 + srcPosition].len = 2; - vec[1 - srcPosition].buffer = (uint8_t*)&portDst; - vec[1 - srcPosition].len = 2; - - - IPv4Layer* ipv4Layer = packet->getLayerOfType(); - if (ipv4Layer != nullptr) - { - if (portSrc == portDst && ipv4Layer->getIPv4Header()->ipDst < ipv4Layer->getIPv4Header()->ipSrc) - srcPosition = 1; - - vec[2 + srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipSrc; - vec[2 + srcPosition].len = 4; - vec[3 - srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipDst; - vec[3 - srcPosition].len = 4; - vec[4].buffer = &(ipv4Layer->getIPv4Header()->protocol); - vec[4].len = 1; - } - else - { - IPv6Layer* ipv6Layer = packet->getLayerOfType(); - if (portSrc == portDst && (uint64_t)ipv6Layer->getIPv6Header()->ipDst < (uint64_t)ipv6Layer->getIPv6Header()->ipSrc) - srcPosition = 1; - - vec[2 + srcPosition].buffer = ipv6Layer->getIPv6Header()->ipSrc; - vec[2 + srcPosition].len = 16; - vec[3 - srcPosition].buffer = ipv6Layer->getIPv6Header()->ipDst; - vec[3 - srcPosition].len = 16; - vec[4].buffer = &(ipv6Layer->getIPv6Header()->nextHeader); - vec[4].len = 1; - } - - return pcpp::fnvHash(vec, 5); -} - - -uint32_t hash2Tuple(Packet* packet) -{ - if (!packet->isPacketOfType(IPv4) && !packet->isPacketOfType(IPv6)) - return 0; - - ScalarBuffer vec[2]; - - IPv4Layer* ipv4Layer = packet->getLayerOfType(); - if (ipv4Layer != nullptr) - { - int srcPosition = 0; - if (ipv4Layer->getIPv4Header()->ipDst < ipv4Layer->getIPv4Header()->ipSrc) - srcPosition = 1; - - vec[0 + srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipSrc; - vec[0 + srcPosition].len = 4; - vec[1 - srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipDst; - vec[1 - srcPosition].len = 4; - } - else - { - IPv6Layer* ipv6Layer = packet->getLayerOfType(); - int srcPosition = 0; - if ((uint64_t)ipv6Layer->getIPv6Header()->ipDst < (uint64_t)ipv6Layer->getIPv6Header()->ipSrc - && (uint64_t)(ipv6Layer->getIPv6Header()->ipDst+8) < (uint64_t)(ipv6Layer->getIPv6Header()->ipSrc+8)) - srcPosition = 1; - - vec[0 + srcPosition].buffer = ipv6Layer->getIPv6Header()->ipSrc; - vec[0 + srcPosition].len = 16; - vec[1 - srcPosition].buffer = ipv6Layer->getIPv6Header()->ipDst; - vec[1 - srcPosition].len = 16; - } - - return pcpp::fnvHash(vec, 2); -} - -} // namespace pcpp +#include + +#include "PacketUtils.h" +#include "IPv4Layer.h" +#include "IPv6Layer.h" +#include "TcpLayer.h" +#include "UdpLayer.h" +#include "IcmpLayer.h" +#include "Logger.h" +#include "EndianPortable.h" + +namespace pcpp +{ + +uint16_t computeChecksum(ScalarBuffer vec[], size_t vecSize) +{ + uint32_t sum = 0; + for (size_t i = 0; i>16) + { + localSum = (localSum & 0xffff) + (localSum >> 16); + } + PCPP_LOG_DEBUG("Local sum = " << localSum << ", 0x" << std::uppercase << std::hex << localSum); + sum += localSum; + } + + while (sum>>16) + { + sum = (sum & 0xffff) + (sum >> 16); + } + PCPP_LOG_DEBUG("Sum before invert = " << sum << ", 0x" << std::uppercase << std::hex << sum); + + // To obtain the checksum we take the ones' complement of this result + uint16_t result = sum; + result = ~result; + + PCPP_LOG_DEBUG("Calculated checksum = " << sum << ", 0x" << std::uppercase << std::hex << result); + + // We return the result in BigEndian byte order + return htobe16(result); +} + +static const uint32_t FNV_PRIME = 16777619u; +static const uint32_t OFFSET_BASIS = 2166136261u; + +uint32_t fnvHash(ScalarBuffer vec[], size_t vecSize) +{ + uint32_t hash = OFFSET_BASIS; + for (size_t i = 0; i < vecSize; ++i) + { + for (size_t j = 0; j < vec[i].len; ++j) + { + hash *= FNV_PRIME; + hash ^= vec[i].buffer[j]; + } + } + return hash; +} + +uint32_t fnvHash(uint8_t* buffer, size_t bufSize) +{ + ScalarBuffer scalarBuf; + scalarBuf.buffer = buffer; + scalarBuf.len = bufSize; + return fnvHash(&scalarBuf, 1); +} + +uint32_t hash5Tuple(Packet* packet, bool const& directionUnique) +{ + if (!packet->isPacketOfType(IPv4) && !packet->isPacketOfType(IPv6)) + return 0; + + if (packet->isPacketOfType(ICMP)) + return 0; + + if (!(packet->isPacketOfType(TCP)) && (!packet->isPacketOfType(UDP))) + return 0; + + ScalarBuffer vec[5]; + + uint16_t portSrc = 0; + uint16_t portDst = 0; + int srcPosition = 0; + + TcpLayer* tcpLayer = packet->getLayerOfType(true); // lookup in reverse order + if (tcpLayer != nullptr) + { + portSrc = tcpLayer->getTcpHeader()->portSrc; + portDst = tcpLayer->getTcpHeader()->portDst; + } + else + { + UdpLayer* udpLayer = packet->getLayerOfType(true); + portSrc = udpLayer->getUdpHeader()->portSrc; + portDst = udpLayer->getUdpHeader()->portDst; + } + + if( ! directionUnique) + { + if (portDst < portSrc) + srcPosition = 1; + } + + vec[0 + srcPosition].buffer = (uint8_t*)&portSrc; + vec[0 + srcPosition].len = 2; + vec[1 - srcPosition].buffer = (uint8_t*)&portDst; + vec[1 - srcPosition].len = 2; + + + IPv4Layer* ipv4Layer = packet->getLayerOfType(); + if (ipv4Layer != nullptr) + { + if (portSrc == portDst && ipv4Layer->getIPv4Header()->ipDst < ipv4Layer->getIPv4Header()->ipSrc) + srcPosition = 1; + + vec[2 + srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipSrc; + vec[2 + srcPosition].len = 4; + vec[3 - srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipDst; + vec[3 - srcPosition].len = 4; + vec[4].buffer = &(ipv4Layer->getIPv4Header()->protocol); + vec[4].len = 1; + } + else + { + IPv6Layer* ipv6Layer = packet->getLayerOfType(); + if (portSrc == portDst && (uint64_t)ipv6Layer->getIPv6Header()->ipDst < (uint64_t)ipv6Layer->getIPv6Header()->ipSrc) + srcPosition = 1; + + vec[2 + srcPosition].buffer = ipv6Layer->getIPv6Header()->ipSrc; + vec[2 + srcPosition].len = 16; + vec[3 - srcPosition].buffer = ipv6Layer->getIPv6Header()->ipDst; + vec[3 - srcPosition].len = 16; + vec[4].buffer = &(ipv6Layer->getIPv6Header()->nextHeader); + vec[4].len = 1; + } + + return pcpp::fnvHash(vec, 5); +} + + +uint32_t hash2Tuple(Packet* packet) +{ + if (!packet->isPacketOfType(IPv4) && !packet->isPacketOfType(IPv6)) + return 0; + + ScalarBuffer vec[2]; + + IPv4Layer* ipv4Layer = packet->getLayerOfType(); + if (ipv4Layer != nullptr) + { + int srcPosition = 0; + if (ipv4Layer->getIPv4Header()->ipDst < ipv4Layer->getIPv4Header()->ipSrc) + srcPosition = 1; + + vec[0 + srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipSrc; + vec[0 + srcPosition].len = 4; + vec[1 - srcPosition].buffer = (uint8_t*)&ipv4Layer->getIPv4Header()->ipDst; + vec[1 - srcPosition].len = 4; + } + else + { + IPv6Layer* ipv6Layer = packet->getLayerOfType(); + int srcPosition = 0; + if ((uint64_t)ipv6Layer->getIPv6Header()->ipDst < (uint64_t)ipv6Layer->getIPv6Header()->ipSrc + && (uint64_t)(ipv6Layer->getIPv6Header()->ipDst+8) < (uint64_t)(ipv6Layer->getIPv6Header()->ipSrc+8)) + srcPosition = 1; + + vec[0 + srcPosition].buffer = ipv6Layer->getIPv6Header()->ipSrc; + vec[0 + srcPosition].len = 16; + vec[1 - srcPosition].buffer = ipv6Layer->getIPv6Header()->ipDst; + vec[1 - srcPosition].len = 16; + } + + return pcpp::fnvHash(vec, 2); +} + +} // namespace pcpp diff --git a/Packet++/src/PayloadLayer.cpp b/Packet++/src/PayloadLayer.cpp index 94fef688d8..8f2344af59 100644 --- a/Packet++/src/PayloadLayer.cpp +++ b/Packet++/src/PayloadLayer.cpp @@ -29,24 +29,24 @@ PayloadLayer::PayloadLayer(const std::string& payloadAsHexStream) } } -void PayloadLayer::setPayload(const uint8_t* newPayload, size_t newPayloadLength) -{ - if (newPayloadLength < m_DataLen) - { - // shorten payload layer - shortenLayer(newPayloadLength, m_DataLen - newPayloadLength); - } - else if (newPayloadLength > m_DataLen) - { - // extend payload layer - extendLayer(m_DataLen, newPayloadLength - m_DataLen); - } - - // and copy data to layer - // this is also executed if the newPayloadLength == m_DataLen - memcpy(m_Data, newPayload, newPayloadLength); -} - +void PayloadLayer::setPayload(const uint8_t* newPayload, size_t newPayloadLength) +{ + if (newPayloadLength < m_DataLen) + { + // shorten payload layer + shortenLayer(newPayloadLength, m_DataLen - newPayloadLength); + } + else if (newPayloadLength > m_DataLen) + { + // extend payload layer + extendLayer(m_DataLen, newPayloadLength - m_DataLen); + } + + // and copy data to layer + // this is also executed if the newPayloadLength == m_DataLen + memcpy(m_Data, newPayload, newPayloadLength); +} + std::string PayloadLayer::toString() const { std::ostringstream dataLenStream; diff --git a/Packet++/src/RadiusLayer.cpp b/Packet++/src/RadiusLayer.cpp index 2ae7df6775..4d4d58f77d 100644 --- a/Packet++/src/RadiusLayer.cpp +++ b/Packet++/src/RadiusLayer.cpp @@ -1,258 +1,258 @@ -#define LOG_MODULE PacketLogModuleRadiusLayer -#include "RadiusLayer.h" -#include "Logger.h" -#include "GeneralUtils.h" - -#include -#include -#include "EndianPortable.h" - -namespace pcpp -{ - -RadiusAttribute RadiusAttributeBuilder::build() const -{ - size_t recSize = m_RecValueLen+2; - uint8_t* recordBuffer = new uint8_t[recSize]; - memset(recordBuffer, 0, recSize); - recordBuffer[0] = static_cast(m_RecType); - recordBuffer[1] = static_cast(recSize); - if (m_RecValueLen > 0) - memcpy(recordBuffer+2, m_RecValue, m_RecValueLen); - - return RadiusAttribute(recordBuffer); -} - -RadiusLayer::RadiusLayer(uint8_t code, uint8_t id, const uint8_t* authenticator, uint8_t authenticatorArrSize) -{ - m_DataLen = sizeof(radius_header); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = Radius; - - radius_header* hdr = getRadiusHeader(); - hdr->code = code; - hdr->id = id; - hdr->length = htobe16(sizeof(radius_header)); - if (authenticatorArrSize == 0 || authenticator == nullptr) - return; - if (authenticatorArrSize > 16) - authenticatorArrSize = 16; - memcpy(hdr->authenticator, authenticator, authenticatorArrSize); -} - -RadiusLayer::RadiusLayer(uint8_t code, uint8_t id, const std::string &authenticator) -{ - m_DataLen = sizeof(radius_header); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = Radius; - - radius_header* hdr = getRadiusHeader(); - hdr->code = code; - hdr->id = id; - hdr->length = htobe16(sizeof(radius_header)); - setAuthenticatorValue(authenticator); -} - -RadiusAttribute RadiusLayer::addAttrAt(const RadiusAttributeBuilder& attrBuilder, int offset) -{ - RadiusAttribute newAttr = attrBuilder.build(); - if (newAttr.isNull()) - { - PCPP_LOG_ERROR("Cannot build new attribute of type " << (int)newAttr.getType()); - return newAttr; - } - - size_t sizeToExtend = newAttr.getTotalSize(); - if (!extendLayer(offset, sizeToExtend)) - { - PCPP_LOG_ERROR("Could not extend RadiusLayer in [" << newAttr.getTotalSize() << "] bytes"); - newAttr.purgeRecordData(); - return RadiusAttribute(nullptr); - } - - memcpy(m_Data + offset, newAttr.getRecordBasePtr(), newAttr.getTotalSize()); - - uint8_t* newAttrPtr = m_Data + offset; - - m_AttributeReader.changeTLVRecordCount(1); - - newAttr.purgeRecordData(); - - getRadiusHeader()->length = htobe16(m_DataLen); - - return RadiusAttribute(newAttrPtr); -} - -std::string RadiusLayer::getAuthenticatorValue() const -{ - return byteArrayToHexString(getRadiusHeader()->authenticator, 16); -} - -void RadiusLayer::setAuthenticatorValue(const std::string& authValue) -{ - hexStringToByteArray(authValue, getRadiusHeader()->authenticator, 16); -} - -std::string RadiusLayer::getRadiusMessageString(uint8_t radiusMessageCode) -{ - switch (radiusMessageCode) - { - case 1: - return "Access-Request"; - case 2: - return "Access-Accept"; - case 3: - return "Access-Reject"; - case 4: - return "Accounting-Request"; - case 5: - return "Accounting-Response"; - case 11: - return "Access-Challenge"; - case 12: - return "Status-Server"; - case 13: - return "Status-Client"; - case 40: - return "Disconnect-Request"; - case 41: - return "Disconnect-ACK"; - case 42: - return "Disconnect-NAK"; - case 43: - return "CoA-Request"; - case 44: - return "CoA-ACK"; - case 45: - return "CoA-NAK"; - case 255: - return "Reserved"; - default: - return "Unknown"; - } -} - -size_t RadiusLayer::getHeaderLen() const -{ - uint16_t len = be16toh(getRadiusHeader()->length); - if (len > m_DataLen) - return m_DataLen; - - return len; -} - -void RadiusLayer::computeCalculateFields() -{ - getRadiusHeader()->length = htobe16(m_DataLen); -} - -std::string RadiusLayer::toString() const -{ - std::ostringstream str; - str << "RADIUS Layer, " << - RadiusLayer::getRadiusMessageString(getRadiusHeader()->code) << - "(" << - (int)getRadiusHeader()->code << - "), " - "Id=" << - (int)getRadiusHeader()->id << - ", " << - "Length=" << - be16toh(getRadiusHeader()->length); - - return str.str(); -} - -RadiusAttribute RadiusLayer::getFirstAttribute() const -{ - return m_AttributeReader.getFirstTLVRecord(getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); -} - -RadiusAttribute RadiusLayer::getNextAttribute(RadiusAttribute& attr) const -{ - return m_AttributeReader.getNextTLVRecord(attr, getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); -} - -RadiusAttribute RadiusLayer::getAttribute(uint8_t attributeType) const -{ - return m_AttributeReader.getTLVRecord(attributeType, getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); -} - -size_t RadiusLayer::getAttributeCount() const -{ - return m_AttributeReader.getTLVRecordCount(getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); -} - -RadiusAttribute RadiusLayer::addAttribute(const RadiusAttributeBuilder& attrBuilder) -{ - int offset = getHeaderLen(); - return addAttrAt(attrBuilder, offset); -} - -RadiusAttribute RadiusLayer::addAttributeAfter(const RadiusAttributeBuilder& attrBuilder, uint8_t prevAttrType) -{ - int offset = 0; - - RadiusAttribute prevAttr = getAttribute(prevAttrType); - - if (prevAttr.isNull()) - { - offset = getHeaderLen(); - } - else - { - offset = prevAttr.getRecordBasePtr() + prevAttr.getTotalSize() - m_Data; - } - - return addAttrAt(attrBuilder, offset); -} - -bool RadiusLayer::removeAttribute(uint8_t attrType) -{ - RadiusAttribute attrToRemove = getAttribute(attrType); - if (attrToRemove.isNull()) - { - return false; - } - - int offset = attrToRemove.getRecordBasePtr() - m_Data; - - if (!shortenLayer(offset, attrToRemove.getTotalSize())) - { - return false; - } - - m_AttributeReader.changeTLVRecordCount(-1); - getRadiusHeader()->length = htobe16(m_DataLen); - - return true; -} - -bool RadiusLayer::removeAllAttributes() -{ - int offset = sizeof(radius_header); - - if (!shortenLayer(offset, getHeaderLen()-offset)) - return false; - - m_AttributeReader.changeTLVRecordCount(0-getAttributeCount()); - - getRadiusHeader()->length = htobe16(m_DataLen); - - return true; -} - -bool RadiusLayer::isDataValid(const uint8_t* udpData, size_t udpDataLen) -{ - if(udpData != nullptr && udpDataLen >= sizeof(radius_header)) - { - const radius_header* radHdr = reinterpret_cast(udpData); - size_t radLen = be16toh(radHdr->length); - return radLen >= sizeof(radius_header) && radLen <= udpDataLen; - } - return false; -} - -} +#define LOG_MODULE PacketLogModuleRadiusLayer +#include "RadiusLayer.h" +#include "Logger.h" +#include "GeneralUtils.h" + +#include +#include +#include "EndianPortable.h" + +namespace pcpp +{ + +RadiusAttribute RadiusAttributeBuilder::build() const +{ + size_t recSize = m_RecValueLen+2; + uint8_t* recordBuffer = new uint8_t[recSize]; + memset(recordBuffer, 0, recSize); + recordBuffer[0] = static_cast(m_RecType); + recordBuffer[1] = static_cast(recSize); + if (m_RecValueLen > 0) + memcpy(recordBuffer+2, m_RecValue, m_RecValueLen); + + return RadiusAttribute(recordBuffer); +} + +RadiusLayer::RadiusLayer(uint8_t code, uint8_t id, const uint8_t* authenticator, uint8_t authenticatorArrSize) +{ + m_DataLen = sizeof(radius_header); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = Radius; + + radius_header* hdr = getRadiusHeader(); + hdr->code = code; + hdr->id = id; + hdr->length = htobe16(sizeof(radius_header)); + if (authenticatorArrSize == 0 || authenticator == nullptr) + return; + if (authenticatorArrSize > 16) + authenticatorArrSize = 16; + memcpy(hdr->authenticator, authenticator, authenticatorArrSize); +} + +RadiusLayer::RadiusLayer(uint8_t code, uint8_t id, const std::string &authenticator) +{ + m_DataLen = sizeof(radius_header); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = Radius; + + radius_header* hdr = getRadiusHeader(); + hdr->code = code; + hdr->id = id; + hdr->length = htobe16(sizeof(radius_header)); + setAuthenticatorValue(authenticator); +} + +RadiusAttribute RadiusLayer::addAttrAt(const RadiusAttributeBuilder& attrBuilder, int offset) +{ + RadiusAttribute newAttr = attrBuilder.build(); + if (newAttr.isNull()) + { + PCPP_LOG_ERROR("Cannot build new attribute of type " << (int)newAttr.getType()); + return newAttr; + } + + size_t sizeToExtend = newAttr.getTotalSize(); + if (!extendLayer(offset, sizeToExtend)) + { + PCPP_LOG_ERROR("Could not extend RadiusLayer in [" << newAttr.getTotalSize() << "] bytes"); + newAttr.purgeRecordData(); + return RadiusAttribute(nullptr); + } + + memcpy(m_Data + offset, newAttr.getRecordBasePtr(), newAttr.getTotalSize()); + + uint8_t* newAttrPtr = m_Data + offset; + + m_AttributeReader.changeTLVRecordCount(1); + + newAttr.purgeRecordData(); + + getRadiusHeader()->length = htobe16(m_DataLen); + + return RadiusAttribute(newAttrPtr); +} + +std::string RadiusLayer::getAuthenticatorValue() const +{ + return byteArrayToHexString(getRadiusHeader()->authenticator, 16); +} + +void RadiusLayer::setAuthenticatorValue(const std::string& authValue) +{ + hexStringToByteArray(authValue, getRadiusHeader()->authenticator, 16); +} + +std::string RadiusLayer::getRadiusMessageString(uint8_t radiusMessageCode) +{ + switch (radiusMessageCode) + { + case 1: + return "Access-Request"; + case 2: + return "Access-Accept"; + case 3: + return "Access-Reject"; + case 4: + return "Accounting-Request"; + case 5: + return "Accounting-Response"; + case 11: + return "Access-Challenge"; + case 12: + return "Status-Server"; + case 13: + return "Status-Client"; + case 40: + return "Disconnect-Request"; + case 41: + return "Disconnect-ACK"; + case 42: + return "Disconnect-NAK"; + case 43: + return "CoA-Request"; + case 44: + return "CoA-ACK"; + case 45: + return "CoA-NAK"; + case 255: + return "Reserved"; + default: + return "Unknown"; + } +} + +size_t RadiusLayer::getHeaderLen() const +{ + uint16_t len = be16toh(getRadiusHeader()->length); + if (len > m_DataLen) + return m_DataLen; + + return len; +} + +void RadiusLayer::computeCalculateFields() +{ + getRadiusHeader()->length = htobe16(m_DataLen); +} + +std::string RadiusLayer::toString() const +{ + std::ostringstream str; + str << "RADIUS Layer, " << + RadiusLayer::getRadiusMessageString(getRadiusHeader()->code) << + "(" << + (int)getRadiusHeader()->code << + "), " + "Id=" << + (int)getRadiusHeader()->id << + ", " << + "Length=" << + be16toh(getRadiusHeader()->length); + + return str.str(); +} + +RadiusAttribute RadiusLayer::getFirstAttribute() const +{ + return m_AttributeReader.getFirstTLVRecord(getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); +} + +RadiusAttribute RadiusLayer::getNextAttribute(RadiusAttribute& attr) const +{ + return m_AttributeReader.getNextTLVRecord(attr, getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); +} + +RadiusAttribute RadiusLayer::getAttribute(uint8_t attributeType) const +{ + return m_AttributeReader.getTLVRecord(attributeType, getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); +} + +size_t RadiusLayer::getAttributeCount() const +{ + return m_AttributeReader.getTLVRecordCount(getAttributesBasePtr(), getHeaderLen() - sizeof(radius_header)); +} + +RadiusAttribute RadiusLayer::addAttribute(const RadiusAttributeBuilder& attrBuilder) +{ + int offset = getHeaderLen(); + return addAttrAt(attrBuilder, offset); +} + +RadiusAttribute RadiusLayer::addAttributeAfter(const RadiusAttributeBuilder& attrBuilder, uint8_t prevAttrType) +{ + int offset = 0; + + RadiusAttribute prevAttr = getAttribute(prevAttrType); + + if (prevAttr.isNull()) + { + offset = getHeaderLen(); + } + else + { + offset = prevAttr.getRecordBasePtr() + prevAttr.getTotalSize() - m_Data; + } + + return addAttrAt(attrBuilder, offset); +} + +bool RadiusLayer::removeAttribute(uint8_t attrType) +{ + RadiusAttribute attrToRemove = getAttribute(attrType); + if (attrToRemove.isNull()) + { + return false; + } + + int offset = attrToRemove.getRecordBasePtr() - m_Data; + + if (!shortenLayer(offset, attrToRemove.getTotalSize())) + { + return false; + } + + m_AttributeReader.changeTLVRecordCount(-1); + getRadiusHeader()->length = htobe16(m_DataLen); + + return true; +} + +bool RadiusLayer::removeAllAttributes() +{ + int offset = sizeof(radius_header); + + if (!shortenLayer(offset, getHeaderLen()-offset)) + return false; + + m_AttributeReader.changeTLVRecordCount(0-getAttributeCount()); + + getRadiusHeader()->length = htobe16(m_DataLen); + + return true; +} + +bool RadiusLayer::isDataValid(const uint8_t* udpData, size_t udpDataLen) +{ + if(udpData != nullptr && udpDataLen >= sizeof(radius_header)) + { + const radius_header* radHdr = reinterpret_cast(udpData); + size_t radLen = be16toh(radHdr->length); + return radLen >= sizeof(radius_header) && radLen <= udpDataLen; + } + return false; +} + +} diff --git a/Packet++/src/RawPacket.cpp b/Packet++/src/RawPacket.cpp index b8ef88ea36..a70faf7592 100644 --- a/Packet++/src/RawPacket.cpp +++ b/Packet++/src/RawPacket.cpp @@ -1,307 +1,307 @@ -#define LOG_MODULE PacketLogModuleRawPacket - -#include "RawPacket.h" -#include -#include "Logger.h" -#include "TimespecTimeval.h" - -namespace pcpp -{ - -void RawPacket::init(bool deleteRawDataAtDestructor) -{ - m_RawData = nullptr; - m_RawDataLen = 0; - m_FrameLength = 0; - m_DeleteRawDataAtDestructor = deleteRawDataAtDestructor; - m_RawPacketSet = false; - m_LinkLayerType = LINKTYPE_ETHERNET; -} - -RawPacket::RawPacket(const uint8_t* pRawData, int rawDataLen, timeval timestamp, bool deleteRawDataAtDestructor, LinkLayerType layerType) -{ - timespec nsec_time; - TIMEVAL_TO_TIMESPEC(×tamp, &nsec_time); - init(deleteRawDataAtDestructor); - setRawData(pRawData, rawDataLen, nsec_time, layerType); -} - -RawPacket::RawPacket(const uint8_t* pRawData, int rawDataLen, timespec timestamp, bool deleteRawDataAtDestructor, LinkLayerType layerType) -{ - init(deleteRawDataAtDestructor); - setRawData(pRawData, rawDataLen, timestamp, layerType); -} - -RawPacket::RawPacket() -{ - init(); -} - -RawPacket::~RawPacket() -{ - if (m_DeleteRawDataAtDestructor) - { - delete[] m_RawData; - } -} - -RawPacket::RawPacket(const RawPacket& other) -{ - m_RawData = nullptr; - copyDataFrom(other, true); -} - -RawPacket& RawPacket::operator=(const RawPacket& other) -{ - if (this != &other) - { - if (m_RawData != nullptr) - delete [] m_RawData; - - m_RawPacketSet = false; - - copyDataFrom(other, true); - } - - return *this; -} - - -void RawPacket::copyDataFrom(const RawPacket& other, bool allocateData) -{ - if (!other.m_RawPacketSet) - return; - - m_TimeStamp = other.m_TimeStamp; - - if (allocateData) - { - m_DeleteRawDataAtDestructor = true; - m_RawData = new uint8_t[other.m_RawDataLen]; - m_RawDataLen = other.m_RawDataLen; - } - - memcpy(m_RawData, other.m_RawData, other.m_RawDataLen); - m_LinkLayerType = other.m_LinkLayerType; - m_FrameLength = other.m_FrameLength; - m_RawPacketSet = true; -} - -bool RawPacket::setRawData(const uint8_t* pRawData, int rawDataLen, timeval timestamp, LinkLayerType layerType, int frameLength) -{ - timespec nsec_time; - TIMEVAL_TO_TIMESPEC(×tamp, &nsec_time); - return setRawData(pRawData, rawDataLen, nsec_time, layerType, frameLength); -} - -bool RawPacket::setRawData(const uint8_t* pRawData, int rawDataLen, timespec timestamp, LinkLayerType layerType, int frameLength) -{ - if(frameLength == -1) - frameLength = rawDataLen; - m_FrameLength = frameLength; - if (m_RawData != nullptr && m_DeleteRawDataAtDestructor) - { - delete[] m_RawData; - } - - m_RawData = (uint8_t*)pRawData; - m_RawDataLen = rawDataLen; - m_TimeStamp = timestamp; - m_RawPacketSet = true; - m_LinkLayerType = layerType; - return true; -} - -void RawPacket::clear() -{ - if (m_RawData != nullptr) - delete[] m_RawData; - - m_RawData = nullptr; - m_RawDataLen = 0; - m_FrameLength = 0; - m_RawPacketSet = false; -} - -void RawPacket::appendData(const uint8_t* dataToAppend, size_t dataToAppendLen) -{ - memcpy((uint8_t*)m_RawData + m_RawDataLen, dataToAppend, dataToAppendLen); - m_RawDataLen += dataToAppendLen; - m_FrameLength = m_RawDataLen; -} - -void RawPacket::insertData(int atIndex, const uint8_t* dataToInsert, size_t dataToInsertLen) -{ - // memmove copies data as if there was an intermediate buffer in between - so it allows for copying processes on overlapping src/dest ptrs - // if insertData is called with atIndex == m_RawDataLen, then no data is being moved. The data of the raw packet is still extended by dataToInsertLen - memmove((uint8_t*)m_RawData + atIndex + dataToInsertLen, (uint8_t*)m_RawData + atIndex, m_RawDataLen - atIndex); - - if (dataToInsert != nullptr) - { - // insert data - memcpy((uint8_t*)m_RawData + atIndex, dataToInsert, dataToInsertLen); - } - - m_RawDataLen += dataToInsertLen; - m_FrameLength = m_RawDataLen; -} - -bool RawPacket::reallocateData(size_t newBufferLength) -{ - if ((int)newBufferLength == m_RawDataLen) - return true; - - if ((int)newBufferLength < m_RawDataLen) - { - PCPP_LOG_ERROR("Cannot reallocate raw packet to a smaller size. Current data length: " << m_RawDataLen << "; requested length: " << newBufferLength); - return false; - } - - uint8_t* newBuffer = new uint8_t[newBufferLength]; - memset(newBuffer, 0, newBufferLength); - memcpy(newBuffer, m_RawData, m_RawDataLen); - if (m_DeleteRawDataAtDestructor) - delete [] m_RawData; - - m_DeleteRawDataAtDestructor = true; - m_RawData = newBuffer; - - return true; -} - -bool RawPacket::removeData(int atIndex, size_t numOfBytesToRemove) -{ - if ((atIndex + (int)numOfBytesToRemove) > m_RawDataLen) - { - PCPP_LOG_ERROR("Remove section is out of raw packet bound"); - return false; - } - - // only move data if we are removing data somewhere in the layer, not at the end of the last layer - // this is so that resizing of the last layer can occur fast by just reducing the fictional length of the packet (m_RawDataLen) by the given amount - if((atIndex + (int)numOfBytesToRemove) != m_RawDataLen) - // memmove copies data as if there was an intermediate buffer in between - so it allows for copying processes on overlapping src/dest ptrs - memmove((uint8_t*)m_RawData + atIndex, (uint8_t*)m_RawData + atIndex + numOfBytesToRemove, m_RawDataLen - (atIndex + numOfBytesToRemove)); - - m_RawDataLen -= numOfBytesToRemove; - m_FrameLength = m_RawDataLen; - return true; -} - -bool RawPacket::setPacketTimeStamp(timeval timestamp) -{ - timespec nsec_time; - TIMEVAL_TO_TIMESPEC(×tamp, &nsec_time); - return setPacketTimeStamp(nsec_time); -} - -bool RawPacket::setPacketTimeStamp(timespec timestamp) -{ - m_TimeStamp = timestamp; - return true; -} - -bool RawPacket::isLinkTypeValid(int linkTypeValue) -{ - if (linkTypeValue < 0 || linkTypeValue > 264) - return false; - - switch (static_cast(linkTypeValue)) - { - case LINKTYPE_ETHERNET: - case LINKTYPE_LINUX_SLL: - case LINKTYPE_RAW: - case LINKTYPE_DLT_RAW1: - case LINKTYPE_DLT_RAW2: - case LINKTYPE_NULL: - case LINKTYPE_AX25: - case LINKTYPE_IEEE802_5: - case LINKTYPE_ARCNET_BSD: - case LINKTYPE_SLIP: - case LINKTYPE_PPP: - case LINKTYPE_FDDI: - case LINKTYPE_PPP_HDLC: - case LINKTYPE_PPP_ETHER: - case LINKTYPE_ATM_RFC1483: - case LINKTYPE_C_HDLC: - case LINKTYPE_IEEE802_11: - case LINKTYPE_FRELAY: - case LINKTYPE_LOOP: - case LINKTYPE_LTALK: - case LINKTYPE_PFLOG: - case LINKTYPE_IEEE802_11_PRISM: - case LINKTYPE_IP_OVER_FC: - case LINKTYPE_SUNATM: - case LINKTYPE_IEEE802_11_RADIOTAP: - case LINKTYPE_ARCNET_LINUX: - case LINKTYPE_APPLE_IP_OVER_IEEE1394: - case LINKTYPE_MTP2_WITH_PHDR: - case LINKTYPE_MTP2: - case LINKTYPE_MTP3: - case LINKTYPE_SCCP: - case LINKTYPE_DOCSIS: - case LINKTYPE_LINUX_IRDA: - case LINKTYPE_IEEE802_11_AVS: - case LINKTYPE_BACNET_MS_TP: - case LINKTYPE_PPP_PPPD: - case LINKTYPE_GPRS_LLC: - case LINKTYPE_GPF_T: - case LINKTYPE_GPF_F: - case LINKTYPE_LINUX_LAPD: - case LINKTYPE_BLUETOOTH_HCI_H4: - case LINKTYPE_USB_LINUX: - case LINKTYPE_PPI: - case LINKTYPE_IEEE802_15_4: - case LINKTYPE_SITA: - case LINKTYPE_ERF: - case LINKTYPE_BLUETOOTH_HCI_H4_WITH_PHDR: - case LINKTYPE_AX25_KISS: - case LINKTYPE_LAPD: - case LINKTYPE_PPP_WITH_DIR: - case LINKTYPE_C_HDLC_WITH_DIR: - case LINKTYPE_FRELAY_WITH_DIR: - case LINKTYPE_IPMB_LINUX: - case LINKTYPE_IEEE802_15_4_NONASK_PHY: - case LINKTYPE_USB_LINUX_MMAPPED: - case LINKTYPE_FC_2: - case LINKTYPE_FC_2_WITH_FRAME_DELIMS: - case LINKTYPE_IPNET: - case LINKTYPE_CAN_SOCKETCAN: - case LINKTYPE_IPV4: - case LINKTYPE_IPV6: - case LINKTYPE_IEEE802_15_4_NOFCS: - case LINKTYPE_DBUS: - case LINKTYPE_DVB_CI: - case LINKTYPE_MUX27010: - case LINKTYPE_STANAG_5066_D_PDU: - case LINKTYPE_NFLOG: - case LINKTYPE_NETANALYZER: - case LINKTYPE_NETANALYZER_TRANSPARENT: - case LINKTYPE_IPOIB: - case LINKTYPE_MPEG_2_TS: - case LINKTYPE_NG40: - case LINKTYPE_NFC_LLCP: - case LINKTYPE_INFINIBAND: - case LINKTYPE_SCTP: - case LINKTYPE_USBPCAP: - case LINKTYPE_RTAC_SERIAL: - case LINKTYPE_BLUETOOTH_LE_LL: - case LINKTYPE_NETLINK: - case LINKTYPE_BLUETOOTH_LINUX_MONITOR: - case LINKTYPE_BLUETOOTH_BREDR_BB: - case LINKTYPE_BLUETOOTH_LE_LL_WITH_PHDR: - case LINKTYPE_PROFIBUS_DL: - case LINKTYPE_PKTAP: - case LINKTYPE_EPON: - case LINKTYPE_IPMI_HPM_2: - case LINKTYPE_ZWAVE_R1_R2: - case LINKTYPE_ZWAVE_R3: - case LINKTYPE_WATTSTOPPER_DLM: - case LINKTYPE_ISO_14443: - return true; - default: - return false; - } -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModuleRawPacket + +#include "RawPacket.h" +#include +#include "Logger.h" +#include "TimespecTimeval.h" + +namespace pcpp +{ + +void RawPacket::init(bool deleteRawDataAtDestructor) +{ + m_RawData = nullptr; + m_RawDataLen = 0; + m_FrameLength = 0; + m_DeleteRawDataAtDestructor = deleteRawDataAtDestructor; + m_RawPacketSet = false; + m_LinkLayerType = LINKTYPE_ETHERNET; +} + +RawPacket::RawPacket(const uint8_t* pRawData, int rawDataLen, timeval timestamp, bool deleteRawDataAtDestructor, LinkLayerType layerType) +{ + timespec nsec_time; + TIMEVAL_TO_TIMESPEC(×tamp, &nsec_time); + init(deleteRawDataAtDestructor); + setRawData(pRawData, rawDataLen, nsec_time, layerType); +} + +RawPacket::RawPacket(const uint8_t* pRawData, int rawDataLen, timespec timestamp, bool deleteRawDataAtDestructor, LinkLayerType layerType) +{ + init(deleteRawDataAtDestructor); + setRawData(pRawData, rawDataLen, timestamp, layerType); +} + +RawPacket::RawPacket() +{ + init(); +} + +RawPacket::~RawPacket() +{ + if (m_DeleteRawDataAtDestructor) + { + delete[] m_RawData; + } +} + +RawPacket::RawPacket(const RawPacket& other) +{ + m_RawData = nullptr; + copyDataFrom(other, true); +} + +RawPacket& RawPacket::operator=(const RawPacket& other) +{ + if (this != &other) + { + if (m_RawData != nullptr) + delete [] m_RawData; + + m_RawPacketSet = false; + + copyDataFrom(other, true); + } + + return *this; +} + + +void RawPacket::copyDataFrom(const RawPacket& other, bool allocateData) +{ + if (!other.m_RawPacketSet) + return; + + m_TimeStamp = other.m_TimeStamp; + + if (allocateData) + { + m_DeleteRawDataAtDestructor = true; + m_RawData = new uint8_t[other.m_RawDataLen]; + m_RawDataLen = other.m_RawDataLen; + } + + memcpy(m_RawData, other.m_RawData, other.m_RawDataLen); + m_LinkLayerType = other.m_LinkLayerType; + m_FrameLength = other.m_FrameLength; + m_RawPacketSet = true; +} + +bool RawPacket::setRawData(const uint8_t* pRawData, int rawDataLen, timeval timestamp, LinkLayerType layerType, int frameLength) +{ + timespec nsec_time; + TIMEVAL_TO_TIMESPEC(×tamp, &nsec_time); + return setRawData(pRawData, rawDataLen, nsec_time, layerType, frameLength); +} + +bool RawPacket::setRawData(const uint8_t* pRawData, int rawDataLen, timespec timestamp, LinkLayerType layerType, int frameLength) +{ + if(frameLength == -1) + frameLength = rawDataLen; + m_FrameLength = frameLength; + if (m_RawData != nullptr && m_DeleteRawDataAtDestructor) + { + delete[] m_RawData; + } + + m_RawData = (uint8_t*)pRawData; + m_RawDataLen = rawDataLen; + m_TimeStamp = timestamp; + m_RawPacketSet = true; + m_LinkLayerType = layerType; + return true; +} + +void RawPacket::clear() +{ + if (m_RawData != nullptr) + delete[] m_RawData; + + m_RawData = nullptr; + m_RawDataLen = 0; + m_FrameLength = 0; + m_RawPacketSet = false; +} + +void RawPacket::appendData(const uint8_t* dataToAppend, size_t dataToAppendLen) +{ + memcpy((uint8_t*)m_RawData + m_RawDataLen, dataToAppend, dataToAppendLen); + m_RawDataLen += dataToAppendLen; + m_FrameLength = m_RawDataLen; +} + +void RawPacket::insertData(int atIndex, const uint8_t* dataToInsert, size_t dataToInsertLen) +{ + // memmove copies data as if there was an intermediate buffer in between - so it allows for copying processes on overlapping src/dest ptrs + // if insertData is called with atIndex == m_RawDataLen, then no data is being moved. The data of the raw packet is still extended by dataToInsertLen + memmove((uint8_t*)m_RawData + atIndex + dataToInsertLen, (uint8_t*)m_RawData + atIndex, m_RawDataLen - atIndex); + + if (dataToInsert != nullptr) + { + // insert data + memcpy((uint8_t*)m_RawData + atIndex, dataToInsert, dataToInsertLen); + } + + m_RawDataLen += dataToInsertLen; + m_FrameLength = m_RawDataLen; +} + +bool RawPacket::reallocateData(size_t newBufferLength) +{ + if ((int)newBufferLength == m_RawDataLen) + return true; + + if ((int)newBufferLength < m_RawDataLen) + { + PCPP_LOG_ERROR("Cannot reallocate raw packet to a smaller size. Current data length: " << m_RawDataLen << "; requested length: " << newBufferLength); + return false; + } + + uint8_t* newBuffer = new uint8_t[newBufferLength]; + memset(newBuffer, 0, newBufferLength); + memcpy(newBuffer, m_RawData, m_RawDataLen); + if (m_DeleteRawDataAtDestructor) + delete [] m_RawData; + + m_DeleteRawDataAtDestructor = true; + m_RawData = newBuffer; + + return true; +} + +bool RawPacket::removeData(int atIndex, size_t numOfBytesToRemove) +{ + if ((atIndex + (int)numOfBytesToRemove) > m_RawDataLen) + { + PCPP_LOG_ERROR("Remove section is out of raw packet bound"); + return false; + } + + // only move data if we are removing data somewhere in the layer, not at the end of the last layer + // this is so that resizing of the last layer can occur fast by just reducing the fictional length of the packet (m_RawDataLen) by the given amount + if((atIndex + (int)numOfBytesToRemove) != m_RawDataLen) + // memmove copies data as if there was an intermediate buffer in between - so it allows for copying processes on overlapping src/dest ptrs + memmove((uint8_t*)m_RawData + atIndex, (uint8_t*)m_RawData + atIndex + numOfBytesToRemove, m_RawDataLen - (atIndex + numOfBytesToRemove)); + + m_RawDataLen -= numOfBytesToRemove; + m_FrameLength = m_RawDataLen; + return true; +} + +bool RawPacket::setPacketTimeStamp(timeval timestamp) +{ + timespec nsec_time; + TIMEVAL_TO_TIMESPEC(×tamp, &nsec_time); + return setPacketTimeStamp(nsec_time); +} + +bool RawPacket::setPacketTimeStamp(timespec timestamp) +{ + m_TimeStamp = timestamp; + return true; +} + +bool RawPacket::isLinkTypeValid(int linkTypeValue) +{ + if (linkTypeValue < 0 || linkTypeValue > 264) + return false; + + switch (static_cast(linkTypeValue)) + { + case LINKTYPE_ETHERNET: + case LINKTYPE_LINUX_SLL: + case LINKTYPE_RAW: + case LINKTYPE_DLT_RAW1: + case LINKTYPE_DLT_RAW2: + case LINKTYPE_NULL: + case LINKTYPE_AX25: + case LINKTYPE_IEEE802_5: + case LINKTYPE_ARCNET_BSD: + case LINKTYPE_SLIP: + case LINKTYPE_PPP: + case LINKTYPE_FDDI: + case LINKTYPE_PPP_HDLC: + case LINKTYPE_PPP_ETHER: + case LINKTYPE_ATM_RFC1483: + case LINKTYPE_C_HDLC: + case LINKTYPE_IEEE802_11: + case LINKTYPE_FRELAY: + case LINKTYPE_LOOP: + case LINKTYPE_LTALK: + case LINKTYPE_PFLOG: + case LINKTYPE_IEEE802_11_PRISM: + case LINKTYPE_IP_OVER_FC: + case LINKTYPE_SUNATM: + case LINKTYPE_IEEE802_11_RADIOTAP: + case LINKTYPE_ARCNET_LINUX: + case LINKTYPE_APPLE_IP_OVER_IEEE1394: + case LINKTYPE_MTP2_WITH_PHDR: + case LINKTYPE_MTP2: + case LINKTYPE_MTP3: + case LINKTYPE_SCCP: + case LINKTYPE_DOCSIS: + case LINKTYPE_LINUX_IRDA: + case LINKTYPE_IEEE802_11_AVS: + case LINKTYPE_BACNET_MS_TP: + case LINKTYPE_PPP_PPPD: + case LINKTYPE_GPRS_LLC: + case LINKTYPE_GPF_T: + case LINKTYPE_GPF_F: + case LINKTYPE_LINUX_LAPD: + case LINKTYPE_BLUETOOTH_HCI_H4: + case LINKTYPE_USB_LINUX: + case LINKTYPE_PPI: + case LINKTYPE_IEEE802_15_4: + case LINKTYPE_SITA: + case LINKTYPE_ERF: + case LINKTYPE_BLUETOOTH_HCI_H4_WITH_PHDR: + case LINKTYPE_AX25_KISS: + case LINKTYPE_LAPD: + case LINKTYPE_PPP_WITH_DIR: + case LINKTYPE_C_HDLC_WITH_DIR: + case LINKTYPE_FRELAY_WITH_DIR: + case LINKTYPE_IPMB_LINUX: + case LINKTYPE_IEEE802_15_4_NONASK_PHY: + case LINKTYPE_USB_LINUX_MMAPPED: + case LINKTYPE_FC_2: + case LINKTYPE_FC_2_WITH_FRAME_DELIMS: + case LINKTYPE_IPNET: + case LINKTYPE_CAN_SOCKETCAN: + case LINKTYPE_IPV4: + case LINKTYPE_IPV6: + case LINKTYPE_IEEE802_15_4_NOFCS: + case LINKTYPE_DBUS: + case LINKTYPE_DVB_CI: + case LINKTYPE_MUX27010: + case LINKTYPE_STANAG_5066_D_PDU: + case LINKTYPE_NFLOG: + case LINKTYPE_NETANALYZER: + case LINKTYPE_NETANALYZER_TRANSPARENT: + case LINKTYPE_IPOIB: + case LINKTYPE_MPEG_2_TS: + case LINKTYPE_NG40: + case LINKTYPE_NFC_LLCP: + case LINKTYPE_INFINIBAND: + case LINKTYPE_SCTP: + case LINKTYPE_USBPCAP: + case LINKTYPE_RTAC_SERIAL: + case LINKTYPE_BLUETOOTH_LE_LL: + case LINKTYPE_NETLINK: + case LINKTYPE_BLUETOOTH_LINUX_MONITOR: + case LINKTYPE_BLUETOOTH_BREDR_BB: + case LINKTYPE_BLUETOOTH_LE_LL_WITH_PHDR: + case LINKTYPE_PROFIBUS_DL: + case LINKTYPE_PKTAP: + case LINKTYPE_EPON: + case LINKTYPE_IPMI_HPM_2: + case LINKTYPE_ZWAVE_R1_R2: + case LINKTYPE_ZWAVE_R3: + case LINKTYPE_WATTSTOPPER_DLM: + case LINKTYPE_ISO_14443: + return true; + default: + return false; + } +} + +} // namespace pcpp diff --git a/Packet++/src/SSLLayer.cpp b/Packet++/src/SSLLayer.cpp index 33dbb95a25..422d0938e2 100644 --- a/Packet++/src/SSLLayer.cpp +++ b/Packet++/src/SSLLayer.cpp @@ -1,258 +1,258 @@ -#define LOG_MODULE PacketLogModuleSSLLayer - -#include "Logger.h" -#include "SSLLayer.h" -#include "EndianPortable.h" -#include - - -namespace pcpp -{ - -// ---------------- -// SSLLayer methods -// ---------------- - -bool SSLLayer::IsSSLMessage(uint16_t srcPort, uint16_t dstPort, uint8_t* data, size_t dataLen, bool ignorePorts) -{ - // check the port map first - if (!ignorePorts && !isSSLPort(srcPort) && !isSSLPort(dstPort)) - return false; - - if (dataLen < sizeof(ssl_tls_record_layer)) - return false; - - ssl_tls_record_layer* recordLayer = (ssl_tls_record_layer*)data; - - // there is no SSL message with length 0 - if (recordLayer->length == 0) - return false; - - if (recordLayer->recordType < 20 || recordLayer->recordType > 23) - return false; - - SSLVersion::SSLVersionEnum recordVersion = SSLVersion(be16toh(recordLayer->recordVersion)).asEnum(true); - - if (recordVersion == SSLVersion::TLS1_3 || - recordVersion == SSLVersion::TLS1_2 || - recordVersion == SSLVersion::TLS1_1 || - recordVersion == SSLVersion::TLS1_0 || - recordVersion == SSLVersion::SSL3) - return true; - else - return false; -} - -SSLLayer* SSLLayer::createSSLMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) -{ - ssl_tls_record_layer* recordLayer = (ssl_tls_record_layer*)data; - switch (recordLayer->recordType) - { - case SSL_HANDSHAKE: - { - return new SSLHandshakeLayer(data, dataLen, prevLayer, packet); - } - - case SSL_ALERT: - { - return new SSLAlertLayer(data, dataLen, prevLayer, packet); - } - - case SSL_CHANGE_CIPHER_SPEC: - { - return new SSLChangeCipherSpecLayer(data, dataLen, prevLayer, packet); - } - - case SSL_APPLICATION_DATA: - { - return new SSLApplicationDataLayer(data, dataLen, prevLayer, packet); - } - - default: - return nullptr; - } -} - -SSLVersion SSLLayer::getRecordVersion() const -{ - uint16_t recordVersion = be16toh(getRecordLayer()->recordVersion); - return SSLVersion(recordVersion); -} - -SSLRecordType SSLLayer::getRecordType() const -{ - return (SSLRecordType)(getRecordLayer()->recordType); -} - -size_t SSLLayer::getHeaderLen() const -{ - size_t len = sizeof(ssl_tls_record_layer) + be16toh(getRecordLayer()->length); - if (len > m_DataLen) - return m_DataLen; - return len; -} - -void SSLLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; - - if (SSLLayer::IsSSLMessage(0, 0, m_Data + headerLen, m_DataLen - headerLen, true)) - m_NextLayer = SSLLayer::createSSLMessage(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); -} - - -// ------------------------- -// SSLHandshakeLayer methods -// ------------------------- - -std::string SSLHandshakeLayer::toString() const -{ - std::stringstream result; - result << getRecordVersion().toString(true) << " Layer, Handshake:"; - for(size_t i = 0; i < m_MessageList.size(); i++) - { - if (i == 0) - result << " " << m_MessageList.at(i)->toString(); - else - result << ", " << m_MessageList.at(i)->toString(); - } - return result.str(); -} - -SSLHandshakeLayer::SSLHandshakeLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) - : SSLLayer(data, dataLen, prevLayer, packet) -{ - uint8_t* curPos = m_Data + sizeof(ssl_tls_record_layer); - size_t recordDataLen = be16toh(getRecordLayer()->length); - if (recordDataLen > m_DataLen - sizeof(ssl_tls_record_layer)) - recordDataLen = m_DataLen - sizeof(ssl_tls_record_layer); - - size_t curPosIndex = 0; - while (true) - { - SSLHandshakeMessage* message = SSLHandshakeMessage::createHandshakeMessage(curPos, recordDataLen-curPosIndex, this); - if (message == nullptr) - break; - - m_MessageList.pushBack(message); - curPos += message->getMessageLength(); - curPosIndex += message->getMessageLength(); - } -} - -SSLHandshakeMessage* SSLHandshakeLayer::getHandshakeMessageAt(int index) const -{ - if (index < 0 || index >= (int)(m_MessageList.size())) - return nullptr; - - return const_cast(m_MessageList.at(index)); -} - - -// -------------------------------- -// SSLChangeCipherSpecLayer methods -// -------------------------------- - -std::string SSLChangeCipherSpecLayer::toString() const -{ - std::stringstream result; - result << getRecordVersion().toString(true) << " Layer, Change Cipher Spec"; - return result.str(); -} - -// --------------------- -// SSLAlertLayer methods -// --------------------- - -SSLAlertLevel SSLAlertLayer::getAlertLevel() const -{ - uint8_t* pos = m_Data + sizeof(ssl_tls_record_layer); - uint8_t alertLevel = *pos; - if (alertLevel == SSL_ALERT_LEVEL_WARNING || alertLevel == SSL_ALERT_LEVEL_FATAL) - return (SSLAlertLevel)alertLevel; - else - return SSL_ALERT_LEVEL_ENCRYPTED; -} - -SSLAlertDescription SSLAlertLayer::getAlertDescription() -{ - if (getAlertLevel() == SSL_ALERT_LEVEL_ENCRYPTED) - return SSL_ALERT_ENCRYPTED; - - uint8_t* pos = m_Data + sizeof(ssl_tls_record_layer) + sizeof(uint8_t); - uint8_t alertDesc = *pos; - - switch (alertDesc) - { - case SSL_ALERT_CLOSE_NOTIFY: - case SSL_ALERT_UNEXPECTED_MESSAGE: - case SSL_ALERT_BAD_RECORD_MAC: - case SSL_ALERT_DECRYPTION_FAILED: - case SSL_ALERT_RECORD_OVERFLOW: - case SSL_ALERT_DECOMPRESSION_FAILURE: - case SSL_ALERT_HANDSHAKE_FAILURE: - case SSL_ALERT_NO_CERTIFICATE: - case SSL_ALERT_BAD_CERTIFICATE: - case SSL_ALERT_UNSUPPORTED_CERTIFICATE: - case SSL_ALERT_CERTIFICATE_REVOKED: - case SSL_ALERT_CERTIFICATE_EXPIRED: - case SSL_ALERT_CERTIFICATE_UNKNOWN: - case SSL_ALERT_ILLEGAL_PARAMETER: - case SSL_ALERT_UNKNOWN_CA: - case SSL_ALERT_ACCESS_DENIED: - case SSL_ALERT_DECODE_ERROR: - case SSL_ALERT_DECRYPT_ERROR: - case SSL_ALERT_EXPORT_RESTRICTION: - case SSL_ALERT_PROTOCOL_VERSION: - case SSL_ALERT_INSUFFICIENT_SECURITY: - case SSL_ALERT_INTERNAL_ERROR: - case SSL_ALERT_USER_CANCELLED: - case SSL_ALERT_NO_RENEGOTIATION: - return (SSLAlertDescription)alertDesc; - break; - default: - return SSL_ALERT_ENCRYPTED; - } -} - -std::string SSLAlertLayer::toString() const -{ - std::stringstream result; - result << getRecordVersion().toString(true) << " Layer, "; - if (getAlertLevel() == SSL_ALERT_LEVEL_ENCRYPTED) - result << "Encrypted Alert"; - else - //TODO: add alert level and description here - result << "Alert"; - return result.str(); -} - -// ------------------------------- -// SSLApplicationDataLayer methods -// ------------------------------- - -uint8_t* SSLApplicationDataLayer::getEncryptedData() const -{ - if (getHeaderLen() <= sizeof(ssl_tls_record_layer)) - return nullptr; - - return m_Data + sizeof(ssl_tls_record_layer); -} - -size_t SSLApplicationDataLayer::getEncryptedDataLen() const -{ - int result = (int)getHeaderLen() - (int)sizeof(ssl_tls_record_layer); - if (result < 0) - return 0; - - return (size_t)result; -} - -std::string SSLApplicationDataLayer::toString() const -{ - return getRecordVersion().toString(true) + " Layer, Application Data"; -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModuleSSLLayer + +#include "Logger.h" +#include "SSLLayer.h" +#include "EndianPortable.h" +#include + + +namespace pcpp +{ + +// ---------------- +// SSLLayer methods +// ---------------- + +bool SSLLayer::IsSSLMessage(uint16_t srcPort, uint16_t dstPort, uint8_t* data, size_t dataLen, bool ignorePorts) +{ + // check the port map first + if (!ignorePorts && !isSSLPort(srcPort) && !isSSLPort(dstPort)) + return false; + + if (dataLen < sizeof(ssl_tls_record_layer)) + return false; + + ssl_tls_record_layer* recordLayer = (ssl_tls_record_layer*)data; + + // there is no SSL message with length 0 + if (recordLayer->length == 0) + return false; + + if (recordLayer->recordType < 20 || recordLayer->recordType > 23) + return false; + + SSLVersion::SSLVersionEnum recordVersion = SSLVersion(be16toh(recordLayer->recordVersion)).asEnum(true); + + if (recordVersion == SSLVersion::TLS1_3 || + recordVersion == SSLVersion::TLS1_2 || + recordVersion == SSLVersion::TLS1_1 || + recordVersion == SSLVersion::TLS1_0 || + recordVersion == SSLVersion::SSL3) + return true; + else + return false; +} + +SSLLayer* SSLLayer::createSSLMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) +{ + ssl_tls_record_layer* recordLayer = (ssl_tls_record_layer*)data; + switch (recordLayer->recordType) + { + case SSL_HANDSHAKE: + { + return new SSLHandshakeLayer(data, dataLen, prevLayer, packet); + } + + case SSL_ALERT: + { + return new SSLAlertLayer(data, dataLen, prevLayer, packet); + } + + case SSL_CHANGE_CIPHER_SPEC: + { + return new SSLChangeCipherSpecLayer(data, dataLen, prevLayer, packet); + } + + case SSL_APPLICATION_DATA: + { + return new SSLApplicationDataLayer(data, dataLen, prevLayer, packet); + } + + default: + return nullptr; + } +} + +SSLVersion SSLLayer::getRecordVersion() const +{ + uint16_t recordVersion = be16toh(getRecordLayer()->recordVersion); + return SSLVersion(recordVersion); +} + +SSLRecordType SSLLayer::getRecordType() const +{ + return (SSLRecordType)(getRecordLayer()->recordType); +} + +size_t SSLLayer::getHeaderLen() const +{ + size_t len = sizeof(ssl_tls_record_layer) + be16toh(getRecordLayer()->length); + if (len > m_DataLen) + return m_DataLen; + return len; +} + +void SSLLayer::parseNextLayer() +{ + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; + + if (SSLLayer::IsSSLMessage(0, 0, m_Data + headerLen, m_DataLen - headerLen, true)) + m_NextLayer = SSLLayer::createSSLMessage(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); +} + + +// ------------------------- +// SSLHandshakeLayer methods +// ------------------------- + +std::string SSLHandshakeLayer::toString() const +{ + std::stringstream result; + result << getRecordVersion().toString(true) << " Layer, Handshake:"; + for(size_t i = 0; i < m_MessageList.size(); i++) + { + if (i == 0) + result << " " << m_MessageList.at(i)->toString(); + else + result << ", " << m_MessageList.at(i)->toString(); + } + return result.str(); +} + +SSLHandshakeLayer::SSLHandshakeLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : SSLLayer(data, dataLen, prevLayer, packet) +{ + uint8_t* curPos = m_Data + sizeof(ssl_tls_record_layer); + size_t recordDataLen = be16toh(getRecordLayer()->length); + if (recordDataLen > m_DataLen - sizeof(ssl_tls_record_layer)) + recordDataLen = m_DataLen - sizeof(ssl_tls_record_layer); + + size_t curPosIndex = 0; + while (true) + { + SSLHandshakeMessage* message = SSLHandshakeMessage::createHandshakeMessage(curPos, recordDataLen-curPosIndex, this); + if (message == nullptr) + break; + + m_MessageList.pushBack(message); + curPos += message->getMessageLength(); + curPosIndex += message->getMessageLength(); + } +} + +SSLHandshakeMessage* SSLHandshakeLayer::getHandshakeMessageAt(int index) const +{ + if (index < 0 || index >= (int)(m_MessageList.size())) + return nullptr; + + return const_cast(m_MessageList.at(index)); +} + + +// -------------------------------- +// SSLChangeCipherSpecLayer methods +// -------------------------------- + +std::string SSLChangeCipherSpecLayer::toString() const +{ + std::stringstream result; + result << getRecordVersion().toString(true) << " Layer, Change Cipher Spec"; + return result.str(); +} + +// --------------------- +// SSLAlertLayer methods +// --------------------- + +SSLAlertLevel SSLAlertLayer::getAlertLevel() const +{ + uint8_t* pos = m_Data + sizeof(ssl_tls_record_layer); + uint8_t alertLevel = *pos; + if (alertLevel == SSL_ALERT_LEVEL_WARNING || alertLevel == SSL_ALERT_LEVEL_FATAL) + return (SSLAlertLevel)alertLevel; + else + return SSL_ALERT_LEVEL_ENCRYPTED; +} + +SSLAlertDescription SSLAlertLayer::getAlertDescription() +{ + if (getAlertLevel() == SSL_ALERT_LEVEL_ENCRYPTED) + return SSL_ALERT_ENCRYPTED; + + uint8_t* pos = m_Data + sizeof(ssl_tls_record_layer) + sizeof(uint8_t); + uint8_t alertDesc = *pos; + + switch (alertDesc) + { + case SSL_ALERT_CLOSE_NOTIFY: + case SSL_ALERT_UNEXPECTED_MESSAGE: + case SSL_ALERT_BAD_RECORD_MAC: + case SSL_ALERT_DECRYPTION_FAILED: + case SSL_ALERT_RECORD_OVERFLOW: + case SSL_ALERT_DECOMPRESSION_FAILURE: + case SSL_ALERT_HANDSHAKE_FAILURE: + case SSL_ALERT_NO_CERTIFICATE: + case SSL_ALERT_BAD_CERTIFICATE: + case SSL_ALERT_UNSUPPORTED_CERTIFICATE: + case SSL_ALERT_CERTIFICATE_REVOKED: + case SSL_ALERT_CERTIFICATE_EXPIRED: + case SSL_ALERT_CERTIFICATE_UNKNOWN: + case SSL_ALERT_ILLEGAL_PARAMETER: + case SSL_ALERT_UNKNOWN_CA: + case SSL_ALERT_ACCESS_DENIED: + case SSL_ALERT_DECODE_ERROR: + case SSL_ALERT_DECRYPT_ERROR: + case SSL_ALERT_EXPORT_RESTRICTION: + case SSL_ALERT_PROTOCOL_VERSION: + case SSL_ALERT_INSUFFICIENT_SECURITY: + case SSL_ALERT_INTERNAL_ERROR: + case SSL_ALERT_USER_CANCELLED: + case SSL_ALERT_NO_RENEGOTIATION: + return (SSLAlertDescription)alertDesc; + break; + default: + return SSL_ALERT_ENCRYPTED; + } +} + +std::string SSLAlertLayer::toString() const +{ + std::stringstream result; + result << getRecordVersion().toString(true) << " Layer, "; + if (getAlertLevel() == SSL_ALERT_LEVEL_ENCRYPTED) + result << "Encrypted Alert"; + else + //TODO: add alert level and description here + result << "Alert"; + return result.str(); +} + +// ------------------------------- +// SSLApplicationDataLayer methods +// ------------------------------- + +uint8_t* SSLApplicationDataLayer::getEncryptedData() const +{ + if (getHeaderLen() <= sizeof(ssl_tls_record_layer)) + return nullptr; + + return m_Data + sizeof(ssl_tls_record_layer); +} + +size_t SSLApplicationDataLayer::getEncryptedDataLen() const +{ + int result = (int)getHeaderLen() - (int)sizeof(ssl_tls_record_layer); + if (result < 0) + return 0; + + return (size_t)result; +} + +std::string SSLApplicationDataLayer::toString() const +{ + return getRecordVersion().toString(true) + " Layer, Application Data"; +} + +} // namespace pcpp diff --git a/Packet++/src/SdpLayer.cpp b/Packet++/src/SdpLayer.cpp index e0e8133620..b90d61c095 100644 --- a/Packet++/src/SdpLayer.cpp +++ b/Packet++/src/SdpLayer.cpp @@ -1,136 +1,136 @@ -#define LOG_MODULE PacketLogModuleSdpLayer - - -#include "SdpLayer.h" -#include "Logger.h" -#include -#include -#include - -namespace pcpp -{ - -std::vector splitByWhiteSpaces(const std::string& str) -{ - std::string buf; - std::stringstream stream(str); - std::vector result; - while (stream >> buf) - result.push_back(buf); - - return result; -} - - -SdpLayer::SdpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : TextBasedProtocolMessage(data, dataLen, prevLayer, packet) -{ - m_Protocol = SDP; - m_FieldsOffset = 0; - parseFields(); -} - -SdpLayer::SdpLayer() -{ - m_Protocol = SDP; - m_FieldsOffset = 0; -} - -SdpLayer::SdpLayer(const std::string& username, long sessionID, long sessionVersion, IPv4Address ipAddress, const std::string& sessionName, long startTime, long stopTime) -{ - m_Protocol = SDP; - m_FieldsOffset = 0; - - // must initialize m_Data otherwise addField() will fail while trying to extend the layer - // initializing in length of 1 but keeping m_DataLen with value of 0. - // when extending the field m_Data is purged so there isn't a memory leak here - m_Data = new uint8_t[1]; - m_DataLen = 0; - - addField(PCPP_SDP_PROTOCOL_VERSION_FIELD, "0"); - - std::stringstream sessionIDStream; - sessionIDStream << sessionID; - std::stringstream sessionVersionStream; - sessionVersionStream << sessionVersion; - std::string networkInfo = "IN IP4 " + ipAddress.toString(); - std::string originatorFieldValue = username + " " + sessionIDStream.str() + " " + sessionVersionStream.str() + " " + networkInfo; - addField(PCPP_SDP_ORIGINATOR_FIELD, originatorFieldValue); - - addField(PCPP_SDP_SESSION_NAME_FIELD, sessionName); - - addField(PCPP_SDP_CONNECTION_INFO_FIELD, networkInfo); - - std::stringstream startTimeStream; - startTimeStream << startTime; - std::stringstream stopTimeStream; - stopTimeStream << stopTime; - addField(PCPP_SDP_TIME_FIELD, startTimeStream.str() + " " + stopTimeStream.str()); -} - -std::string SdpLayer::toString() const -{ - return "SDP Layer"; -} - -IPv4Address SdpLayer::getOwnerIPv4Address() const -{ - HeaderField* originator = getFieldByName(PCPP_SDP_ORIGINATOR_FIELD); - if (originator == nullptr) - return IPv4Address::Zero; - - std::vector tokens = splitByWhiteSpaces(originator->getFieldValue()); - if (tokens.size() < 6) - return IPv4Address::Zero; - - if (tokens[3] != "IN" || tokens[4] != "IP4") - return IPv4Address::Zero; - - return IPv4Address(tokens[5]); -} - -uint16_t SdpLayer::getMediaPort(const std::string& mediaType) const -{ - int mediaFieldIndex = 0; - HeaderField* mediaDesc = getFieldByName(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldIndex); - - while (mediaDesc != nullptr) - { - std::vector tokens = splitByWhiteSpaces(mediaDesc->getFieldValue()); - - if (tokens.size() >= 2 && tokens[0] == mediaType) - return atoi(tokens[1].c_str()); - - mediaFieldIndex++; - mediaDesc = getFieldByName(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldIndex); - } - - return 0; -} - -bool SdpLayer::addMediaDescription(const std::string& mediaType, uint16_t mediaPort, const std::string& mediaProtocol, const std::string& mediaFormat, std::vector mediaAttributes) -{ - std::stringstream portStream; - portStream << mediaPort; - - std::string mediaFieldValue = mediaType + " " + portStream.str() + " " + mediaProtocol + " " + mediaFormat; - if (addField(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldValue) == nullptr) - { - PCPP_LOG_ERROR("Failed to add media description field"); - return false; - } - - - for (std::vector::iterator iter = mediaAttributes.begin(); iter != mediaAttributes.end(); iter++) - { - if (addField(PCPP_SDP_MEDIA_ATTRIBUTE_FIELD, *iter) == nullptr) - { - PCPP_LOG_ERROR("Failed to add media attribute '" << *iter << "'"); - return false; - } - } - - return true; -} - - -} +#define LOG_MODULE PacketLogModuleSdpLayer + + +#include "SdpLayer.h" +#include "Logger.h" +#include +#include +#include + +namespace pcpp +{ + +std::vector splitByWhiteSpaces(const std::string& str) +{ + std::string buf; + std::stringstream stream(str); + std::vector result; + while (stream >> buf) + result.push_back(buf); + + return result; +} + + +SdpLayer::SdpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : TextBasedProtocolMessage(data, dataLen, prevLayer, packet) +{ + m_Protocol = SDP; + m_FieldsOffset = 0; + parseFields(); +} + +SdpLayer::SdpLayer() +{ + m_Protocol = SDP; + m_FieldsOffset = 0; +} + +SdpLayer::SdpLayer(const std::string& username, long sessionID, long sessionVersion, IPv4Address ipAddress, const std::string& sessionName, long startTime, long stopTime) +{ + m_Protocol = SDP; + m_FieldsOffset = 0; + + // must initialize m_Data otherwise addField() will fail while trying to extend the layer + // initializing in length of 1 but keeping m_DataLen with value of 0. + // when extending the field m_Data is purged so there isn't a memory leak here + m_Data = new uint8_t[1]; + m_DataLen = 0; + + addField(PCPP_SDP_PROTOCOL_VERSION_FIELD, "0"); + + std::stringstream sessionIDStream; + sessionIDStream << sessionID; + std::stringstream sessionVersionStream; + sessionVersionStream << sessionVersion; + std::string networkInfo = "IN IP4 " + ipAddress.toString(); + std::string originatorFieldValue = username + " " + sessionIDStream.str() + " " + sessionVersionStream.str() + " " + networkInfo; + addField(PCPP_SDP_ORIGINATOR_FIELD, originatorFieldValue); + + addField(PCPP_SDP_SESSION_NAME_FIELD, sessionName); + + addField(PCPP_SDP_CONNECTION_INFO_FIELD, networkInfo); + + std::stringstream startTimeStream; + startTimeStream << startTime; + std::stringstream stopTimeStream; + stopTimeStream << stopTime; + addField(PCPP_SDP_TIME_FIELD, startTimeStream.str() + " " + stopTimeStream.str()); +} + +std::string SdpLayer::toString() const +{ + return "SDP Layer"; +} + +IPv4Address SdpLayer::getOwnerIPv4Address() const +{ + HeaderField* originator = getFieldByName(PCPP_SDP_ORIGINATOR_FIELD); + if (originator == nullptr) + return IPv4Address::Zero; + + std::vector tokens = splitByWhiteSpaces(originator->getFieldValue()); + if (tokens.size() < 6) + return IPv4Address::Zero; + + if (tokens[3] != "IN" || tokens[4] != "IP4") + return IPv4Address::Zero; + + return IPv4Address(tokens[5]); +} + +uint16_t SdpLayer::getMediaPort(const std::string& mediaType) const +{ + int mediaFieldIndex = 0; + HeaderField* mediaDesc = getFieldByName(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldIndex); + + while (mediaDesc != nullptr) + { + std::vector tokens = splitByWhiteSpaces(mediaDesc->getFieldValue()); + + if (tokens.size() >= 2 && tokens[0] == mediaType) + return atoi(tokens[1].c_str()); + + mediaFieldIndex++; + mediaDesc = getFieldByName(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldIndex); + } + + return 0; +} + +bool SdpLayer::addMediaDescription(const std::string& mediaType, uint16_t mediaPort, const std::string& mediaProtocol, const std::string& mediaFormat, std::vector mediaAttributes) +{ + std::stringstream portStream; + portStream << mediaPort; + + std::string mediaFieldValue = mediaType + " " + portStream.str() + " " + mediaProtocol + " " + mediaFormat; + if (addField(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldValue) == nullptr) + { + PCPP_LOG_ERROR("Failed to add media description field"); + return false; + } + + + for (std::vector::iterator iter = mediaAttributes.begin(); iter != mediaAttributes.end(); iter++) + { + if (addField(PCPP_SDP_MEDIA_ATTRIBUTE_FIELD, *iter) == nullptr) + { + PCPP_LOG_ERROR("Failed to add media attribute '" << *iter << "'"); + return false; + } + } + + return true; +} + + +} diff --git a/Packet++/src/SipLayer.cpp b/Packet++/src/SipLayer.cpp index d51da68d7a..0bfb601be5 100644 --- a/Packet++/src/SipLayer.cpp +++ b/Packet++/src/SipLayer.cpp @@ -1,1292 +1,1292 @@ -#define LOG_MODULE PacketLogModuleSipLayer - -#include "SipLayer.h" -#include "SdpLayer.h" -#include "PayloadLayer.h" -#include "Logger.h" -#include "GeneralUtils.h" -#include -#include -#include -#include +#define LOG_MODULE PacketLogModuleSipLayer + +#include "SipLayer.h" +#include "SdpLayer.h" +#include "PayloadLayer.h" +#include "Logger.h" +#include "GeneralUtils.h" +#include +#include +#include +#include #include - -namespace pcpp -{ - -const std::string SipMethodEnumToString[14] = { - "INVITE", - "ACK", - "BYE", - "CANCEL", - "REGISTER", - "PRACK", - "OPTIONS", - "SUBSCRIBE", - "NOTIFY", - "PUBLISH", - "INFO", - "REFER", - "MESSAGE", - "UPDATE" -}; - - - - -// -------- Class SipLayer ----------------- - -int SipLayer::getContentLength() const -{ - std::string contentLengthFieldName(PCPP_SIP_CONTENT_LENGTH_FIELD); - std::transform(contentLengthFieldName.begin(), contentLengthFieldName.end(), contentLengthFieldName.begin(), ::tolower); - HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); - if (contentLengthField != nullptr) - return atoi(contentLengthField->getFieldValue().c_str()); - return 0; -} - -HeaderField* SipLayer::setContentLength(int contentLength, const std::string &prevFieldName) -{ - std::ostringstream contentLengthAsString; - contentLengthAsString << contentLength; - std::string contentLengthFieldName(PCPP_SIP_CONTENT_LENGTH_FIELD); - HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); - if (contentLengthField == nullptr) - { - HeaderField* prevField = getFieldByName(prevFieldName); - contentLengthField = insertField(prevField, PCPP_SIP_CONTENT_LENGTH_FIELD, contentLengthAsString.str()); - } - else - contentLengthField->setFieldValue(contentLengthAsString.str()); - - return contentLengthField; -} - -void SipLayer::parseNextLayer() -{ - if (getLayerPayloadSize() == 0) - return; - - size_t headerLen = getHeaderLen(); - if (getContentLength() > 0) - { - m_NextLayer = new SdpLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); - } - else - { - m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); - } -} - -void SipLayer::computeCalculateFields() -{ - HeaderField* contentLengthField = getFieldByName(PCPP_SIP_CONTENT_LENGTH_FIELD); - if (contentLengthField == nullptr) - return; - - size_t headerLen = getHeaderLen(); - if (m_DataLen > headerLen) - { - int currentContentLength = getContentLength(); - if (currentContentLength != (int)(m_DataLen - headerLen)) - setContentLength(m_DataLen - headerLen); - } -} - - - - - - - - -// -------- Class SipRequestFirstLine ----------------- - -SipRequestFirstLine::SipRequestFirstLine(SipRequestLayer* sipRequest) : m_SipRequest(sipRequest) -{ - m_Method = parseMethod((char*)m_SipRequest->m_Data, m_SipRequest->getDataLen()); - if (m_Method == SipRequestLayer::SipMethodUnknown) - { - m_UriOffset = -1; - PCPP_LOG_DEBUG("Couldn't resolve SIP request method"); - } - else - m_UriOffset = SipMethodEnumToString[m_Method].length() + 1; - - parseVersion(); - - char* endOfFirstLine; - if ((endOfFirstLine = (char *)memchr((char*)(m_SipRequest->m_Data + m_VersionOffset), '\n', m_SipRequest->m_DataLen-(size_t)m_VersionOffset)) != nullptr) - { - m_FirstLineEndOffset = endOfFirstLine - (char*)m_SipRequest->m_Data + 1; - m_IsComplete = true; - } - else - { - m_FirstLineEndOffset = m_SipRequest->getDataLen(); - m_IsComplete = false; - } - - if (Logger::getInstance().isDebugEnabled(PacketLogModuleSipLayer)) - { - std::string method = (m_Method == SipRequestLayer::SipMethodUnknown? "Unknown" : SipMethodEnumToString[m_Method]); - PCPP_LOG_DEBUG("Method='" << method << "'; SIP version='" << m_Version << "'; URI='" << getUri() << "'"); - } -} - -SipRequestFirstLine::SipRequestFirstLine(SipRequestLayer* sipRequest, SipRequestLayer::SipMethod method, const std::string& version, const std::string& uri) -try // throw(SipRequestFirstLineException) -{ - if (method == SipRequestLayer::SipMethodUnknown) - { - m_Exception.setMessage("Method supplied was SipMethodUnknown"); - throw m_Exception; - } - - if (version == "") - { - m_Exception.setMessage("Version supplied was empty string"); - throw m_Exception; - } - - m_SipRequest = sipRequest; - - m_Method = method; - m_Version = version; - - std::string firstLine = SipMethodEnumToString[m_Method] + " " + uri + " " + version + "\r\n"; - - m_UriOffset = SipMethodEnumToString[m_Method].length() + 1; - m_FirstLineEndOffset = firstLine.length(); - m_VersionOffset = m_UriOffset + uri.length() + 6; - - m_SipRequest->m_DataLen = firstLine.length(); - m_SipRequest->m_Data = new uint8_t[m_SipRequest->m_DataLen]; - memcpy(m_SipRequest->m_Data, firstLine.c_str(), m_SipRequest->m_DataLen); - - m_IsComplete = true; -} -catch(const SipRequestFirstLineException&) -{ - throw; -} -catch(...) -{ - std::terminate(); -} -SipRequestLayer::SipMethod SipRequestFirstLine::parseMethod(char* data, size_t dataLen) -{ - if (dataLen < 4) - { - return SipRequestLayer::SipMethodUnknown; - } - - switch (data[0]) - { - case 'A': - if (data[1] == 'C' && data[2] == 'K' && data[3] == ' ') - return SipRequestLayer::SipACK; - else - return SipRequestLayer::SipMethodUnknown; - break; - - case 'B': - if (data[1] == 'Y' && data[2] == 'E' && data[3] == ' ') - return SipRequestLayer::SipBYE; - else - return SipRequestLayer::SipMethodUnknown; - break; - - case 'C': - if (dataLen < 7) - return SipRequestLayer::SipMethodUnknown; - else if (data[1] == 'A' && data[2] == 'N' && data[3] == 'C' && data[4] == 'E' && data[5] == 'L' && data[6] == ' ') - return SipRequestLayer::SipCANCEL; - else - return SipRequestLayer::SipMethodUnknown; - break; - - case 'O': - if (dataLen < 8) - return SipRequestLayer::SipMethodUnknown; - else if (data[1] == 'P' && data[2] == 'T' && data[3] == 'I' && data[4] == 'O' && data[5] == 'N' && data[6] == 'S' && data[7] == ' ') - return SipRequestLayer::SipOPTIONS; - else - return SipRequestLayer::SipMethodUnknown; - break; - - - case 'R': - if (dataLen < 6) - return SipRequestLayer::SipMethodUnknown; - else if (data[1] == 'E' && data[2] == 'F' && data[3] == 'E' && data[4] == 'R' && data[5] == ' ') - return SipRequestLayer::SipREFER; - else if (dataLen < 9) - return SipRequestLayer::SipMethodUnknown; - else if (data[1] == 'E' && data[2] == 'G' && data[3] == 'I' && data[4] == 'S' && data[5] == 'T' && data[6] == 'E' && data[7] == 'R' && data[8] == ' ') - return SipRequestLayer::SipREGISTER; - else - return SipRequestLayer::SipMethodUnknown; - break; - - case 'P': - if (dataLen < 6) - return SipRequestLayer::SipMethodUnknown; - else if (data[1] == 'R' && data[2] == 'A' && data[3] == 'C' && data[4] == 'K' && data[5] == ' ') - return SipRequestLayer::SipPRACK; - else if (dataLen < 8) - return SipRequestLayer::SipMethodUnknown; - else if (data[1] == 'U' && data[2] == 'B' && data[3] == 'L' && data[4] == 'I' && data[5] == 'S' && data[6] == 'H' && data[7] == ' ') - return SipRequestLayer::SipPUBLISH; - break; - - case 'S': - if (dataLen < 10) - return SipRequestLayer::SipMethodUnknown; - - else if (data[1] == 'U' && data[2] == 'B' && data[3] == 'S' && data[4] == 'C' && data[5] == 'R' && data[6] == 'I' && data[7] == 'B' && data[8] == 'E' && data[9] == ' ') - return SipRequestLayer::SipSUBSCRIBE; - break; - - case 'N': - if (dataLen < 7) - return SipRequestLayer::SipMethodUnknown; - - else if (data[1] == 'O' && data[2] == 'T' && data[3] == 'I' && data[4] == 'F' && data[5] == 'Y' && data[6] == ' ') - return SipRequestLayer::SipNOTIFY; - break; - - case 'I': - if (data[1] == 'N' && data[2] == 'F' && data[3] == 'O') - return SipRequestLayer::SipINFO; - else if (dataLen < 7) - return SipRequestLayer::SipMethodUnknown; - else if (data[1] == 'N' && data[2] == 'V' && data[3] == 'I' && data[4] == 'T' && data[5] == 'E' && data[6] == ' ') - return SipRequestLayer::SipINVITE; - break; - - case 'M': - if (dataLen < 8) - return SipRequestLayer::SipMethodUnknown; - - else if (data[1] == 'E' && data[2] == 'S' && data[3] == 'S' && data[4] == 'A' && data[5] == 'G' && data[6] == 'E' && data[7] == ' ') - return SipRequestLayer::SipMESSAGE; - break; - - case 'U': - if (dataLen < 7) - return SipRequestLayer::SipMethodUnknown; - - else if (data[1] == 'P' && data[2] == 'D' && data[3] == 'A' && data[4] == 'T' && data[5] == 'E' && data[6] == ' ') - return SipRequestLayer::SipUPDATE; - break; - - - default: - return SipRequestLayer::SipMethodUnknown; - } - - return SipRequestLayer::SipMethodUnknown; -} - -void SipRequestFirstLine::parseVersion() -{ - if (m_SipRequest->getDataLen() < m_UriOffset) - { - m_Version = ""; - m_VersionOffset = -1; - return; - } - - char* data = (char*)(m_SipRequest->m_Data + m_UriOffset); - char* verPos = (char*)cross_platform_memmem(data, m_SipRequest->getDataLen() - m_UriOffset, " SIP/", 5); - if (verPos == nullptr) - { - m_Version = ""; - m_VersionOffset = -1; - return; - } - - // verify packet doesn't end before the version, meaning still left place for " SIP/x.y" (7 chars) - if ((uint16_t)(verPos + 7 - (char*)m_SipRequest->m_Data) > m_SipRequest->getDataLen()) - { - m_Version = ""; - m_VersionOffset = -1; - return; - } - - //skip the space char - verPos++; - - int endOfVerPos = 0; - while (((verPos + endOfVerPos) < (char *) (m_SipRequest->m_Data + m_SipRequest->m_DataLen)) && ((verPos+endOfVerPos)[0] != '\r') && ((verPos+endOfVerPos)[0] != '\n')) - endOfVerPos++; - - m_Version = std::string(verPos, endOfVerPos); - - m_VersionOffset = verPos - (char*)m_SipRequest->m_Data; -} - -bool SipRequestFirstLine::setMethod(SipRequestLayer::SipMethod newMethod) -{ - if (newMethod == SipRequestLayer::SipMethodUnknown) - { - PCPP_LOG_ERROR("Requested method is SipMethodUnknown"); - return false; - } - - //extend or shorten layer - int lengthDifference = SipMethodEnumToString[newMethod].length() - SipMethodEnumToString[m_Method].length(); - if (lengthDifference > 0) - { - if (!m_SipRequest->extendLayer(0, lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - else if (lengthDifference < 0) - { - if (!m_SipRequest->shortenLayer(0, 0-lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - - } - } - - if (lengthDifference != 0) - { - m_SipRequest->shiftFieldsOffset(m_SipRequest->getFirstField(), lengthDifference); - m_SipRequest->m_FieldsOffset += lengthDifference; - } - - memcpy(m_SipRequest->m_Data, SipMethodEnumToString[newMethod].c_str(), SipMethodEnumToString[newMethod].length()); - - m_UriOffset += lengthDifference; - m_VersionOffset += lengthDifference; - m_FirstLineEndOffset += lengthDifference; - - m_Method = newMethod; - - return true; -} - -std::string SipRequestFirstLine::getUri() const -{ - std::string result; - if (m_UriOffset != -1 && m_VersionOffset != -1) - result.assign((char*)(m_SipRequest->m_Data + m_UriOffset), m_VersionOffset-1-m_UriOffset); - - //else first line is illegal, return empty string - - return result; -} - -bool SipRequestFirstLine::setUri(const std::string& newUri) -{ - if (newUri == "") - { - PCPP_LOG_ERROR("URI cannot be empty"); - return false; - } - - //extend or shorten layer - std::string currentUri = getUri(); - int lengthDifference = newUri.length() - currentUri.length(); - if (lengthDifference > 0) - { - if (!m_SipRequest->extendLayer(m_UriOffset, lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - else if (lengthDifference < 0) - { - if (!m_SipRequest->shortenLayer(m_UriOffset, 0-lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - - if (lengthDifference != 0) - { - m_SipRequest->shiftFieldsOffset(m_SipRequest->getFirstField(), lengthDifference); - m_SipRequest->m_FieldsOffset += lengthDifference; - } - - memcpy(m_SipRequest->m_Data + m_UriOffset, newUri.c_str(), newUri.length()); - - m_VersionOffset += lengthDifference; - m_FirstLineEndOffset += lengthDifference; - - return true; -} - - - - - -// -------- Class SipRequestLayer ----------------- - -SipRequestLayer::SipRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : SipLayer(data, dataLen, prevLayer, packet) -{ - m_Protocol = SIPRequest; - m_FirstLine = new SipRequestFirstLine(this); - m_FieldsOffset = m_FirstLine->getSize(); - parseFields(); -} - -SipRequestLayer::SipRequestLayer(SipMethod method, const std::string& requestUri, const std::string& version) -{ - m_Protocol = SIPRequest; - m_FirstLine = new SipRequestFirstLine(this, method, std::move(version), std::move(requestUri)); - m_FieldsOffset = m_FirstLine->getSize(); -} - -SipRequestLayer::SipRequestLayer(const SipRequestLayer& other) : SipLayer(other) -{ - m_FirstLine = new SipRequestFirstLine(this); -} - -SipRequestLayer& SipRequestLayer::operator=(const SipRequestLayer& other) -{ - SipLayer::operator=(other); - - if (m_FirstLine != nullptr) - delete m_FirstLine; - - m_FirstLine = new SipRequestFirstLine(this); - - return *this; -} - -SipRequestLayer::~SipRequestLayer() -{ - delete m_FirstLine; -} - -std::string SipRequestLayer::toString() const -{ - static const int maxLengthToPrint = 120; - std::string result = "SIP request, "; - int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line - if (size <= 0) - { - result += std::string("CORRUPT DATA"); - return result; - } - if (size <= maxLengthToPrint) - { - char* firstLine = new char[size+1]; - strncpy(firstLine, (char*)m_Data, size); - firstLine[size] = 0; - result += std::string(firstLine); - delete[] firstLine; - } - else - { - char firstLine[maxLengthToPrint+1]; - strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3); - firstLine[maxLengthToPrint-3] = '.'; - firstLine[maxLengthToPrint-2] = '.'; - firstLine[maxLengthToPrint-1] = '.'; - firstLine[maxLengthToPrint] = 0; - result += std::string(firstLine); - } - - return result; -} - - - - - - -// -------- Class SipResponseLayer ----------------- - - - -const std::string StatusCodeEnumToString[74] = { - "Trying", - "Ringing", - "Call is Being Forwarded", - "Queued", - "Session in Progress", - "Early Dialog Terminated", - "OK", - "Accepted", - "No Notification", - "Multiple Choices", - "Moved Permanently", - "Moved Temporarily", - "Use Proxy", - "Alternative Service", - "Bad Request", - "Unauthorized", - "Payment Required", - "Forbidden", - "Not Found", - "Method Not Allowed", - "Not Acceptable", - "Proxy Authentication Required", - "Request Timeout", - "Conflict", - "Gone", - "Length Required", - "Conditional Request Failed", - "Request Entity Too Large", - "Request-URI Too Long", - "Unsupported Media Type", - "Unsupported URI Scheme", - "Unknown Resource-Priority", - "Bad Extension", - "Extension Required", - "Session Interval Too Small", - "Interval Too Brief", - "Bad Location Information", - "Use Identity Header", - "Provide Referrer Identity", - "Flow Failed", - "Anonymity Disallowed", - "Bad Identity-Info", - "Unsupported Certificate", - "Invalid Identity Header", - "First Hop Lacks Outbound Support", - "Max-Breadth Exceeded", - "Bad Info Package", - "Consent Needed", - "Temporarily Unavailable", - "Call_Transaction Does Not Exist", - "Loop Detected", - "Too Many Hops", - "Address Incomplete", - "Ambiguous", - "Busy Here", - "Request Terminated", - "Not Acceptable Here", - "Bad Event", - "Request Pending", - "Undecipherable", - "Security Agreement Required", - "Server Internal Error", - "Not Implemented", - "Bad Gateway", - "Service Unavailable", - "Server Timeout", - "Version Not Supported", - "Message Too Large", - "Precondition Failure", - "Busy Everywhere", - "Decline", - "Does Not Exist Anywhere", - "Not Acceptable", - "Unwanted" -}; - - -const int StatusCodeEnumToInt[74] = { - 100, - 180, - 181, - 182, - 183, - 199, - 200, - 202, - 204, - 300, - 301, - 302, - 305, - 380, - 400, - 401, - 402, - 403, - 404, - 405, - 406, - 407, - 408, - 409, - 410, - 411, - 412, - 413, - 414, - 415, - 416, - 417, - 420, - 421, - 422, - 423, - 424, - 428, - 429, - 430, - 433, - 436, - 437, - 438, - 439, - 440, - 469, - 470, - 480, - 481, - 482, - 483, - 484, - 485, - 486, - 487, - 488, - 489, - 491, - 493, - 494, - 500, - 501, - 502, - 503, - 504, - 505, - 513, - 580, - 600, - 603, - 604, - 606, - 607 -}; - - - -SipResponseLayer::SipResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : SipLayer(data, dataLen, prevLayer, packet) -{ - m_Protocol = SIPResponse; - m_FirstLine = new SipResponseFirstLine(this); - m_FieldsOffset = m_FirstLine->getSize(); - parseFields(); -} - -SipResponseLayer::SipResponseLayer(SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString, const std::string& sipVersion) -{ - m_Protocol = SIPResponse; - m_FirstLine = new SipResponseFirstLine(this, std::move(sipVersion), statusCode, std::move(statusCodeString)); - m_FieldsOffset = m_FirstLine->getSize(); -} - -SipResponseLayer::~SipResponseLayer() -{ - delete m_FirstLine; -} - - -SipResponseLayer::SipResponseLayer(const SipResponseLayer& other) : SipLayer(other) -{ - m_FirstLine = new SipResponseFirstLine(this); -} - -SipResponseLayer& SipResponseLayer::operator=(const SipResponseLayer& other) -{ - SipLayer::operator=(other); - - if (m_FirstLine != nullptr) - delete m_FirstLine; - - m_FirstLine = new SipResponseFirstLine(this); - - return *this; -} - -std::string SipResponseLayer::toString() const -{ - static const int maxLengthToPrint = 120; - std::string result = "SIP response, "; - int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line - if (size <= 0) - { - result += std::string("CORRUPT DATA"); - return result; - } - if (size <= maxLengthToPrint) - { - char* firstLine = new char[size+1]; - strncpy(firstLine, (char*)m_Data, size); - firstLine[size] = 0; - result += std::string(firstLine); - delete[] firstLine; - } - else - { - char firstLine[maxLengthToPrint+1]; - strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3); - firstLine[maxLengthToPrint-3] = '.'; - firstLine[maxLengthToPrint-2] = '.'; - firstLine[maxLengthToPrint-1] = '.'; - firstLine[maxLengthToPrint] = 0; - result += std::string(firstLine); - } - - return result; -} - - - - - - - - -// -------- Class SipResponseFirstLine ----------------- - -int SipResponseFirstLine::getStatusCodeAsInt() const -{ - return StatusCodeEnumToInt[m_StatusCode]; -} - -std::string SipResponseFirstLine::getStatusCodeString() const -{ - std::string result; - const int statusStringOffset = 12; - if (m_StatusCode != SipResponseLayer::SipStatusCodeUnknown) - { - int statusStringEndOffset = m_FirstLineEndOffset - 2; - if ((*(m_SipResponse->m_Data + statusStringEndOffset)) != '\r') - statusStringEndOffset++; - result.assign((char*)(m_SipResponse->m_Data + statusStringOffset), statusStringEndOffset-statusStringOffset); - } - - //else first line is illegal, return empty string - - return result; -} - -bool SipResponseFirstLine::setStatusCode(SipResponseLayer::SipResponseStatusCode newStatusCode, std::string statusCodeString) -{ - if (newStatusCode == SipResponseLayer::SipStatusCodeUnknown) - { - PCPP_LOG_ERROR("Requested status code is SipStatusCodeUnknown"); - return false; - } - - //extend or shorten layer - - size_t statusStringOffset = 12; - if (statusCodeString == "") - statusCodeString = StatusCodeEnumToString[newStatusCode]; - int lengthDifference = statusCodeString.length() - getStatusCodeString().length(); - - if (lengthDifference > 0) - { - if (!m_SipResponse->extendLayer(statusStringOffset, lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - } - } - else if (lengthDifference < 0) - { - if (!m_SipResponse->shortenLayer(statusStringOffset, 0-lengthDifference)) - { - PCPP_LOG_ERROR("Cannot change layer size"); - return false; - - } - } - - if (lengthDifference != 0) - { - m_SipResponse->shiftFieldsOffset(m_SipResponse->getFirstField(), lengthDifference); - m_SipResponse->m_FieldsOffset += lengthDifference; - } - - // copy status string - memcpy(m_SipResponse->m_Data+statusStringOffset, statusCodeString.c_str(), statusCodeString.length()); - - // change status code - std::ostringstream statusCodeAsString; - statusCodeAsString << StatusCodeEnumToInt[newStatusCode]; - memcpy(m_SipResponse->m_Data+8, statusCodeAsString.str().c_str(), 3); - - m_StatusCode = newStatusCode; - m_FirstLineEndOffset += lengthDifference; - - return true; - -} - -void SipResponseFirstLine::setVersion(const std::string& newVersion) -{ - if (newVersion == "") - return; - - if (newVersion.length() != m_Version.length()) - { - PCPP_LOG_ERROR("Expected version length is " << m_Version.length() << " characters in the format of SIP/x.y"); - return; - } - - char* verPos = (char*)m_SipResponse->m_Data; - memcpy(verPos, newVersion.c_str(), newVersion.length()); - m_Version = newVersion; -} - -SipResponseLayer::SipResponseStatusCode SipResponseFirstLine::validateStatusCode(char* data, size_t dataLen, SipResponseLayer::SipResponseStatusCode potentialCode) -{ - if (data[0] != ' ') - return SipResponseLayer::SipStatusCodeUnknown; - - return potentialCode; -} - -SipResponseLayer::SipResponseStatusCode SipResponseFirstLine::parseStatusCode(char* data, size_t dataLen) -{ - // minimum data should be 12B long: "SIP/x.y XXX" - if (dataLen < 12) - return SipResponseLayer::SipStatusCodeUnknown; - - char* statusCodeData = data + 8; - size_t statusCodeDataLen = dataLen - 8; - - switch (statusCodeData[0]) - { - case '1': - switch (statusCodeData[1]) - { - case '0': - if (statusCodeData[2] == '0') - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip100Trying); - else - return SipResponseLayer::SipStatusCodeUnknown; - - break; - case '8': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip180Ringing); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip181CallisBeingForwarded); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip182Queued); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip183SessioninProgress); - default: - return SipResponseLayer::SipStatusCodeUnknown; - }; - break; - - case '9': - if (statusCodeData[2] == '9') - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip199EarlyDialogTerminated); - else - return SipResponseLayer::SipStatusCodeUnknown; - break; - - default: - return SipResponseLayer::SipStatusCodeUnknown; - }; - - break; - case '2': - if (statusCodeData[1] == '0') - { - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip200OK); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip202Accepted); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip204NoNotification); - default: - return SipResponseLayer::SipStatusCodeUnknown; - - }; - } - else - return SipResponseLayer::SipStatusCodeUnknown; - - break; - - case '3': - switch (statusCodeData[1]) - { - case '0': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip300MultipleChoices); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip301MovedPermanently); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip302MovedTemporarily); - case '5': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip305UseProxy); - default: - return SipResponseLayer::SipStatusCodeUnknown; - - }; - - break; - - case '8': - if (statusCodeData[2] == '0') - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip380AlternativeService); - else - return SipResponseLayer::SipStatusCodeUnknown; - - break; - - default: - return SipResponseLayer::SipStatusCodeUnknown; - }; - - break; - - case '4': - switch (statusCodeData[1]) - { - case '0': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip400BadRequest); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip401Unauthorized); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip402PaymentRequired); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip403Forbidden); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip404NotFound); - case '5': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip405MethodNotAllowed); - case '6': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip406NotAcceptable); - case '7': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip407ProxyAuthenticationRequired); - case '8': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip408RequestTimeout); - case '9': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip409Conflict); - default: - return SipResponseLayer::SipStatusCodeUnknown; - - }; - - break; - - case '1': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip410Gone); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip411LengthRequired); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip412ConditionalRequestFailed); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip413RequestEntityTooLarge); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip414RequestURITooLong); - case '5': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip415UnsupportedMediaType); - case '6': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip416UnsupportedURIScheme); - case '7': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip417UnknownResourcePriority); - case '8': - default: - return SipResponseLayer::SipStatusCodeUnknown; - - }; - - break; - - case '2': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip420BadExtension); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip421ExtensionRequired); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip422SessionIntervalTooSmall); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip423IntervalTooBrief); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip424BadLocationInformation); - case '8': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip428UseIdentityHeader); - case '9': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip429ProvideReferrerIdentity); - default: - return SipResponseLayer::SipStatusCodeUnknown; - - }; - - break; - - case '3': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip430FlowFailed); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip433AnonymityDisallowed); - case '6': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip436BadIdentityInfo); - case '7': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip437UnsupportedCertificate); - case '8': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip438InvalidIdentityHeader); - case '9': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip439FirstHopLacksOutboundSupport); - default: - return SipResponseLayer::SipStatusCodeUnknown; - }; - - break; - - case '4': - if (statusCodeData[2] == '0') - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip440MaxBreadthExceeded); - else - return SipResponseLayer::SipStatusCodeUnknown; - - break; - - case '6': - if (statusCodeData[2] == '9') - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip469BadInfoPackage); - else - return SipResponseLayer::SipStatusCodeUnknown; - - break; - - case '8': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip480TemporarilyUnavailable); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip481Call_TransactionDoesNotExist); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip482LoopDetected); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip483TooManyHops); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip484AddressIncomplete); - case '5': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip485Ambiguous); - case '6': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip486BusyHere); - case '7': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip487RequestTerminated); - case '8': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip488NotAcceptableHere); - case '9': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip489BadEvent); - default: - return SipResponseLayer::SipStatusCodeUnknown; - }; - - break; - - case '9': - switch (statusCodeData[2]) - { - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip491RequestPending); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip493Undecipherable); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip494SecurityAgreementRequired); - default: - return SipResponseLayer::SipStatusCodeUnknown; - }; - - break; - - default: - return SipResponseLayer::SipStatusCodeUnknown; - }; - - break; - - case '5': - switch (statusCodeData[1]) - { - case '0': - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip500ServerInternalError); - case '1': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip501NotImplemented); - case '2': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip502BadGateway); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip503ServiceUnavailable); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip504ServerTimeout); - case '5': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip505VersionNotSupported); - default: - return SipResponseLayer::SipStatusCodeUnknown; - - }; - - break; - - case '1': - if (statusCodeData[2] == '3') - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip513MessageTooLarge); - else - return SipResponseLayer::SipStatusCodeUnknown; - - break; - - case '8': - if (statusCodeData[2] == '0') - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip580PreconditionFailure); - else - return SipResponseLayer::SipStatusCodeUnknown; - - break; - - default: - return SipResponseLayer::SipStatusCodeUnknown; - }; - - break; - - case '6': - if (statusCodeData[1] == '0') - { - switch (statusCodeData[2]) - { - case '0': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip600BusyEverywhere); - case '3': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip603Decline); - case '4': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip604DoesNotExistAnywhere); - case '6': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip606NotAcceptable); - case '7': - return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip607Unwanted); - default: - return SipResponseLayer::SipStatusCodeUnknown; - }; - } - else - return SipResponseLayer::SipStatusCodeUnknown; - - break; - - default: - return SipResponseLayer::SipStatusCodeUnknown; - }; - - return SipResponseLayer::SipStatusCodeUnknown; -} - -SipResponseFirstLine::SipResponseFirstLine(SipResponseLayer* sipResponse) : m_SipResponse(sipResponse) -{ - m_Version = parseVersion((char*)m_SipResponse->m_Data, m_SipResponse->getDataLen()); - if (m_Version == "") - { - m_StatusCode = SipResponseLayer::SipStatusCodeUnknown; - } - else - { - m_StatusCode = parseStatusCode((char*)m_SipResponse->m_Data, m_SipResponse->getDataLen()); - } - - - char* endOfFirstLine; - if ((endOfFirstLine = (char *)memchr((char*)(m_SipResponse->m_Data), '\n', m_SipResponse->m_DataLen)) != nullptr) - { - m_FirstLineEndOffset = endOfFirstLine - (char*)m_SipResponse->m_Data + 1; - m_IsComplete = true; - } - else - { - m_FirstLineEndOffset = m_SipResponse->getDataLen(); - m_IsComplete = false; - } - - if (Logger::getInstance().isDebugEnabled(PacketLogModuleSipLayer)) - { - int statusCode = (m_StatusCode == SipResponseLayer::SipStatusCodeUnknown ? 0 : StatusCodeEnumToInt[m_StatusCode]); - PCPP_LOG_DEBUG("Version='" << m_Version << "'; Status code=" << statusCode << " '" << getStatusCodeString() << "'"); - } -} - - -SipResponseFirstLine::SipResponseFirstLine(SipResponseLayer* sipResponse, const std::string& version, SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString) -{ - if (statusCode == SipResponseLayer::SipStatusCodeUnknown) - { - m_Exception.setMessage("Status code supplied was SipStatusCodeUnknown"); - throw m_Exception; - } - - if (version == "") - { - m_Exception.setMessage("Version supplied was unknown"); - throw m_Exception; - } - - m_SipResponse = sipResponse; - - m_StatusCode = statusCode; - m_Version = version; - - std::ostringstream statusCodeAsString; - statusCodeAsString << StatusCodeEnumToInt[m_StatusCode]; - if (statusCodeString == "") - statusCodeString = StatusCodeEnumToString[m_StatusCode]; - std::string firstLine = m_Version + " " + statusCodeAsString.str() + " " + statusCodeString + "\r\n"; - - m_FirstLineEndOffset = firstLine.length(); - - m_SipResponse->m_DataLen = firstLine.length(); - m_SipResponse->m_Data = new uint8_t[m_SipResponse->m_DataLen]; - memcpy(m_SipResponse->m_Data, firstLine.c_str(), m_SipResponse->m_DataLen); - - m_IsComplete = true; -} - -std::string SipResponseFirstLine::parseVersion(char* data, size_t dataLen) -{ - if (dataLen < 7) // "SIP/x.y" - { - PCPP_LOG_DEBUG("SIP response length < 7, cannot identify version"); - return ""; - } - - if (data[0] != 'S' || data[1] != 'I' || data[2] != 'P' || data[3] != '/') - { - PCPP_LOG_DEBUG("SIP response does not begin with 'SIP/'"); - return ""; - } - - char* nextSpace = (char*)memchr(data, ' ', dataLen); - if (nextSpace == nullptr) - return ""; - - return std::string(data, nextSpace - data); -} - - -} + +namespace pcpp +{ + +const std::string SipMethodEnumToString[14] = { + "INVITE", + "ACK", + "BYE", + "CANCEL", + "REGISTER", + "PRACK", + "OPTIONS", + "SUBSCRIBE", + "NOTIFY", + "PUBLISH", + "INFO", + "REFER", + "MESSAGE", + "UPDATE" +}; + + + + +// -------- Class SipLayer ----------------- + +int SipLayer::getContentLength() const +{ + std::string contentLengthFieldName(PCPP_SIP_CONTENT_LENGTH_FIELD); + std::transform(contentLengthFieldName.begin(), contentLengthFieldName.end(), contentLengthFieldName.begin(), ::tolower); + HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); + if (contentLengthField != nullptr) + return atoi(contentLengthField->getFieldValue().c_str()); + return 0; +} + +HeaderField* SipLayer::setContentLength(int contentLength, const std::string &prevFieldName) +{ + std::ostringstream contentLengthAsString; + contentLengthAsString << contentLength; + std::string contentLengthFieldName(PCPP_SIP_CONTENT_LENGTH_FIELD); + HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); + if (contentLengthField == nullptr) + { + HeaderField* prevField = getFieldByName(prevFieldName); + contentLengthField = insertField(prevField, PCPP_SIP_CONTENT_LENGTH_FIELD, contentLengthAsString.str()); + } + else + contentLengthField->setFieldValue(contentLengthAsString.str()); + + return contentLengthField; +} + +void SipLayer::parseNextLayer() +{ + if (getLayerPayloadSize() == 0) + return; + + size_t headerLen = getHeaderLen(); + if (getContentLength() > 0) + { + m_NextLayer = new SdpLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); + } + else + { + m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet); + } +} + +void SipLayer::computeCalculateFields() +{ + HeaderField* contentLengthField = getFieldByName(PCPP_SIP_CONTENT_LENGTH_FIELD); + if (contentLengthField == nullptr) + return; + + size_t headerLen = getHeaderLen(); + if (m_DataLen > headerLen) + { + int currentContentLength = getContentLength(); + if (currentContentLength != (int)(m_DataLen - headerLen)) + setContentLength(m_DataLen - headerLen); + } +} + + + + + + + + +// -------- Class SipRequestFirstLine ----------------- + +SipRequestFirstLine::SipRequestFirstLine(SipRequestLayer* sipRequest) : m_SipRequest(sipRequest) +{ + m_Method = parseMethod((char*)m_SipRequest->m_Data, m_SipRequest->getDataLen()); + if (m_Method == SipRequestLayer::SipMethodUnknown) + { + m_UriOffset = -1; + PCPP_LOG_DEBUG("Couldn't resolve SIP request method"); + } + else + m_UriOffset = SipMethodEnumToString[m_Method].length() + 1; + + parseVersion(); + + char* endOfFirstLine; + if ((endOfFirstLine = (char *)memchr((char*)(m_SipRequest->m_Data + m_VersionOffset), '\n', m_SipRequest->m_DataLen-(size_t)m_VersionOffset)) != nullptr) + { + m_FirstLineEndOffset = endOfFirstLine - (char*)m_SipRequest->m_Data + 1; + m_IsComplete = true; + } + else + { + m_FirstLineEndOffset = m_SipRequest->getDataLen(); + m_IsComplete = false; + } + + if (Logger::getInstance().isDebugEnabled(PacketLogModuleSipLayer)) + { + std::string method = (m_Method == SipRequestLayer::SipMethodUnknown? "Unknown" : SipMethodEnumToString[m_Method]); + PCPP_LOG_DEBUG("Method='" << method << "'; SIP version='" << m_Version << "'; URI='" << getUri() << "'"); + } +} + +SipRequestFirstLine::SipRequestFirstLine(SipRequestLayer* sipRequest, SipRequestLayer::SipMethod method, const std::string& version, const std::string& uri) +try // throw(SipRequestFirstLineException) +{ + if (method == SipRequestLayer::SipMethodUnknown) + { + m_Exception.setMessage("Method supplied was SipMethodUnknown"); + throw m_Exception; + } + + if (version == "") + { + m_Exception.setMessage("Version supplied was empty string"); + throw m_Exception; + } + + m_SipRequest = sipRequest; + + m_Method = method; + m_Version = version; + + std::string firstLine = SipMethodEnumToString[m_Method] + " " + uri + " " + version + "\r\n"; + + m_UriOffset = SipMethodEnumToString[m_Method].length() + 1; + m_FirstLineEndOffset = firstLine.length(); + m_VersionOffset = m_UriOffset + uri.length() + 6; + + m_SipRequest->m_DataLen = firstLine.length(); + m_SipRequest->m_Data = new uint8_t[m_SipRequest->m_DataLen]; + memcpy(m_SipRequest->m_Data, firstLine.c_str(), m_SipRequest->m_DataLen); + + m_IsComplete = true; +} +catch(const SipRequestFirstLineException&) +{ + throw; +} +catch(...) +{ + std::terminate(); +} +SipRequestLayer::SipMethod SipRequestFirstLine::parseMethod(char* data, size_t dataLen) +{ + if (dataLen < 4) + { + return SipRequestLayer::SipMethodUnknown; + } + + switch (data[0]) + { + case 'A': + if (data[1] == 'C' && data[2] == 'K' && data[3] == ' ') + return SipRequestLayer::SipACK; + else + return SipRequestLayer::SipMethodUnknown; + break; + + case 'B': + if (data[1] == 'Y' && data[2] == 'E' && data[3] == ' ') + return SipRequestLayer::SipBYE; + else + return SipRequestLayer::SipMethodUnknown; + break; + + case 'C': + if (dataLen < 7) + return SipRequestLayer::SipMethodUnknown; + else if (data[1] == 'A' && data[2] == 'N' && data[3] == 'C' && data[4] == 'E' && data[5] == 'L' && data[6] == ' ') + return SipRequestLayer::SipCANCEL; + else + return SipRequestLayer::SipMethodUnknown; + break; + + case 'O': + if (dataLen < 8) + return SipRequestLayer::SipMethodUnknown; + else if (data[1] == 'P' && data[2] == 'T' && data[3] == 'I' && data[4] == 'O' && data[5] == 'N' && data[6] == 'S' && data[7] == ' ') + return SipRequestLayer::SipOPTIONS; + else + return SipRequestLayer::SipMethodUnknown; + break; + + + case 'R': + if (dataLen < 6) + return SipRequestLayer::SipMethodUnknown; + else if (data[1] == 'E' && data[2] == 'F' && data[3] == 'E' && data[4] == 'R' && data[5] == ' ') + return SipRequestLayer::SipREFER; + else if (dataLen < 9) + return SipRequestLayer::SipMethodUnknown; + else if (data[1] == 'E' && data[2] == 'G' && data[3] == 'I' && data[4] == 'S' && data[5] == 'T' && data[6] == 'E' && data[7] == 'R' && data[8] == ' ') + return SipRequestLayer::SipREGISTER; + else + return SipRequestLayer::SipMethodUnknown; + break; + + case 'P': + if (dataLen < 6) + return SipRequestLayer::SipMethodUnknown; + else if (data[1] == 'R' && data[2] == 'A' && data[3] == 'C' && data[4] == 'K' && data[5] == ' ') + return SipRequestLayer::SipPRACK; + else if (dataLen < 8) + return SipRequestLayer::SipMethodUnknown; + else if (data[1] == 'U' && data[2] == 'B' && data[3] == 'L' && data[4] == 'I' && data[5] == 'S' && data[6] == 'H' && data[7] == ' ') + return SipRequestLayer::SipPUBLISH; + break; + + case 'S': + if (dataLen < 10) + return SipRequestLayer::SipMethodUnknown; + + else if (data[1] == 'U' && data[2] == 'B' && data[3] == 'S' && data[4] == 'C' && data[5] == 'R' && data[6] == 'I' && data[7] == 'B' && data[8] == 'E' && data[9] == ' ') + return SipRequestLayer::SipSUBSCRIBE; + break; + + case 'N': + if (dataLen < 7) + return SipRequestLayer::SipMethodUnknown; + + else if (data[1] == 'O' && data[2] == 'T' && data[3] == 'I' && data[4] == 'F' && data[5] == 'Y' && data[6] == ' ') + return SipRequestLayer::SipNOTIFY; + break; + + case 'I': + if (data[1] == 'N' && data[2] == 'F' && data[3] == 'O') + return SipRequestLayer::SipINFO; + else if (dataLen < 7) + return SipRequestLayer::SipMethodUnknown; + else if (data[1] == 'N' && data[2] == 'V' && data[3] == 'I' && data[4] == 'T' && data[5] == 'E' && data[6] == ' ') + return SipRequestLayer::SipINVITE; + break; + + case 'M': + if (dataLen < 8) + return SipRequestLayer::SipMethodUnknown; + + else if (data[1] == 'E' && data[2] == 'S' && data[3] == 'S' && data[4] == 'A' && data[5] == 'G' && data[6] == 'E' && data[7] == ' ') + return SipRequestLayer::SipMESSAGE; + break; + + case 'U': + if (dataLen < 7) + return SipRequestLayer::SipMethodUnknown; + + else if (data[1] == 'P' && data[2] == 'D' && data[3] == 'A' && data[4] == 'T' && data[5] == 'E' && data[6] == ' ') + return SipRequestLayer::SipUPDATE; + break; + + + default: + return SipRequestLayer::SipMethodUnknown; + } + + return SipRequestLayer::SipMethodUnknown; +} + +void SipRequestFirstLine::parseVersion() +{ + if (m_SipRequest->getDataLen() < m_UriOffset) + { + m_Version = ""; + m_VersionOffset = -1; + return; + } + + char* data = (char*)(m_SipRequest->m_Data + m_UriOffset); + char* verPos = (char*)cross_platform_memmem(data, m_SipRequest->getDataLen() - m_UriOffset, " SIP/", 5); + if (verPos == nullptr) + { + m_Version = ""; + m_VersionOffset = -1; + return; + } + + // verify packet doesn't end before the version, meaning still left place for " SIP/x.y" (7 chars) + if ((uint16_t)(verPos + 7 - (char*)m_SipRequest->m_Data) > m_SipRequest->getDataLen()) + { + m_Version = ""; + m_VersionOffset = -1; + return; + } + + //skip the space char + verPos++; + + int endOfVerPos = 0; + while (((verPos + endOfVerPos) < (char *) (m_SipRequest->m_Data + m_SipRequest->m_DataLen)) && ((verPos+endOfVerPos)[0] != '\r') && ((verPos+endOfVerPos)[0] != '\n')) + endOfVerPos++; + + m_Version = std::string(verPos, endOfVerPos); + + m_VersionOffset = verPos - (char*)m_SipRequest->m_Data; +} + +bool SipRequestFirstLine::setMethod(SipRequestLayer::SipMethod newMethod) +{ + if (newMethod == SipRequestLayer::SipMethodUnknown) + { + PCPP_LOG_ERROR("Requested method is SipMethodUnknown"); + return false; + } + + //extend or shorten layer + int lengthDifference = SipMethodEnumToString[newMethod].length() - SipMethodEnumToString[m_Method].length(); + if (lengthDifference > 0) + { + if (!m_SipRequest->extendLayer(0, lengthDifference)) + { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + else if (lengthDifference < 0) + { + if (!m_SipRequest->shortenLayer(0, 0-lengthDifference)) + { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + + } + } + + if (lengthDifference != 0) + { + m_SipRequest->shiftFieldsOffset(m_SipRequest->getFirstField(), lengthDifference); + m_SipRequest->m_FieldsOffset += lengthDifference; + } + + memcpy(m_SipRequest->m_Data, SipMethodEnumToString[newMethod].c_str(), SipMethodEnumToString[newMethod].length()); + + m_UriOffset += lengthDifference; + m_VersionOffset += lengthDifference; + m_FirstLineEndOffset += lengthDifference; + + m_Method = newMethod; + + return true; +} + +std::string SipRequestFirstLine::getUri() const +{ + std::string result; + if (m_UriOffset != -1 && m_VersionOffset != -1) + result.assign((char*)(m_SipRequest->m_Data + m_UriOffset), m_VersionOffset-1-m_UriOffset); + + //else first line is illegal, return empty string + + return result; +} + +bool SipRequestFirstLine::setUri(const std::string& newUri) +{ + if (newUri == "") + { + PCPP_LOG_ERROR("URI cannot be empty"); + return false; + } + + //extend or shorten layer + std::string currentUri = getUri(); + int lengthDifference = newUri.length() - currentUri.length(); + if (lengthDifference > 0) + { + if (!m_SipRequest->extendLayer(m_UriOffset, lengthDifference)) + { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + else if (lengthDifference < 0) + { + if (!m_SipRequest->shortenLayer(m_UriOffset, 0-lengthDifference)) + { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + + if (lengthDifference != 0) + { + m_SipRequest->shiftFieldsOffset(m_SipRequest->getFirstField(), lengthDifference); + m_SipRequest->m_FieldsOffset += lengthDifference; + } + + memcpy(m_SipRequest->m_Data + m_UriOffset, newUri.c_str(), newUri.length()); + + m_VersionOffset += lengthDifference; + m_FirstLineEndOffset += lengthDifference; + + return true; +} + + + + + +// -------- Class SipRequestLayer ----------------- + +SipRequestLayer::SipRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : SipLayer(data, dataLen, prevLayer, packet) +{ + m_Protocol = SIPRequest; + m_FirstLine = new SipRequestFirstLine(this); + m_FieldsOffset = m_FirstLine->getSize(); + parseFields(); +} + +SipRequestLayer::SipRequestLayer(SipMethod method, const std::string& requestUri, const std::string& version) +{ + m_Protocol = SIPRequest; + m_FirstLine = new SipRequestFirstLine(this, method, std::move(version), std::move(requestUri)); + m_FieldsOffset = m_FirstLine->getSize(); +} + +SipRequestLayer::SipRequestLayer(const SipRequestLayer& other) : SipLayer(other) +{ + m_FirstLine = new SipRequestFirstLine(this); +} + +SipRequestLayer& SipRequestLayer::operator=(const SipRequestLayer& other) +{ + SipLayer::operator=(other); + + if (m_FirstLine != nullptr) + delete m_FirstLine; + + m_FirstLine = new SipRequestFirstLine(this); + + return *this; +} + +SipRequestLayer::~SipRequestLayer() +{ + delete m_FirstLine; +} + +std::string SipRequestLayer::toString() const +{ + static const int maxLengthToPrint = 120; + std::string result = "SIP request, "; + int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line + if (size <= 0) + { + result += std::string("CORRUPT DATA"); + return result; + } + if (size <= maxLengthToPrint) + { + char* firstLine = new char[size+1]; + strncpy(firstLine, (char*)m_Data, size); + firstLine[size] = 0; + result += std::string(firstLine); + delete[] firstLine; + } + else + { + char firstLine[maxLengthToPrint+1]; + strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3); + firstLine[maxLengthToPrint-3] = '.'; + firstLine[maxLengthToPrint-2] = '.'; + firstLine[maxLengthToPrint-1] = '.'; + firstLine[maxLengthToPrint] = 0; + result += std::string(firstLine); + } + + return result; +} + + + + + + +// -------- Class SipResponseLayer ----------------- + + + +const std::string StatusCodeEnumToString[74] = { + "Trying", + "Ringing", + "Call is Being Forwarded", + "Queued", + "Session in Progress", + "Early Dialog Terminated", + "OK", + "Accepted", + "No Notification", + "Multiple Choices", + "Moved Permanently", + "Moved Temporarily", + "Use Proxy", + "Alternative Service", + "Bad Request", + "Unauthorized", + "Payment Required", + "Forbidden", + "Not Found", + "Method Not Allowed", + "Not Acceptable", + "Proxy Authentication Required", + "Request Timeout", + "Conflict", + "Gone", + "Length Required", + "Conditional Request Failed", + "Request Entity Too Large", + "Request-URI Too Long", + "Unsupported Media Type", + "Unsupported URI Scheme", + "Unknown Resource-Priority", + "Bad Extension", + "Extension Required", + "Session Interval Too Small", + "Interval Too Brief", + "Bad Location Information", + "Use Identity Header", + "Provide Referrer Identity", + "Flow Failed", + "Anonymity Disallowed", + "Bad Identity-Info", + "Unsupported Certificate", + "Invalid Identity Header", + "First Hop Lacks Outbound Support", + "Max-Breadth Exceeded", + "Bad Info Package", + "Consent Needed", + "Temporarily Unavailable", + "Call_Transaction Does Not Exist", + "Loop Detected", + "Too Many Hops", + "Address Incomplete", + "Ambiguous", + "Busy Here", + "Request Terminated", + "Not Acceptable Here", + "Bad Event", + "Request Pending", + "Undecipherable", + "Security Agreement Required", + "Server Internal Error", + "Not Implemented", + "Bad Gateway", + "Service Unavailable", + "Server Timeout", + "Version Not Supported", + "Message Too Large", + "Precondition Failure", + "Busy Everywhere", + "Decline", + "Does Not Exist Anywhere", + "Not Acceptable", + "Unwanted" +}; + + +const int StatusCodeEnumToInt[74] = { + 100, + 180, + 181, + 182, + 183, + 199, + 200, + 202, + 204, + 300, + 301, + 302, + 305, + 380, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 420, + 421, + 422, + 423, + 424, + 428, + 429, + 430, + 433, + 436, + 437, + 438, + 439, + 440, + 469, + 470, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 491, + 493, + 494, + 500, + 501, + 502, + 503, + 504, + 505, + 513, + 580, + 600, + 603, + 604, + 606, + 607 +}; + + + +SipResponseLayer::SipResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : SipLayer(data, dataLen, prevLayer, packet) +{ + m_Protocol = SIPResponse; + m_FirstLine = new SipResponseFirstLine(this); + m_FieldsOffset = m_FirstLine->getSize(); + parseFields(); +} + +SipResponseLayer::SipResponseLayer(SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString, const std::string& sipVersion) +{ + m_Protocol = SIPResponse; + m_FirstLine = new SipResponseFirstLine(this, std::move(sipVersion), statusCode, std::move(statusCodeString)); + m_FieldsOffset = m_FirstLine->getSize(); +} + +SipResponseLayer::~SipResponseLayer() +{ + delete m_FirstLine; +} + + +SipResponseLayer::SipResponseLayer(const SipResponseLayer& other) : SipLayer(other) +{ + m_FirstLine = new SipResponseFirstLine(this); +} + +SipResponseLayer& SipResponseLayer::operator=(const SipResponseLayer& other) +{ + SipLayer::operator=(other); + + if (m_FirstLine != nullptr) + delete m_FirstLine; + + m_FirstLine = new SipResponseFirstLine(this); + + return *this; +} + +std::string SipResponseLayer::toString() const +{ + static const int maxLengthToPrint = 120; + std::string result = "SIP response, "; + int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line + if (size <= 0) + { + result += std::string("CORRUPT DATA"); + return result; + } + if (size <= maxLengthToPrint) + { + char* firstLine = new char[size+1]; + strncpy(firstLine, (char*)m_Data, size); + firstLine[size] = 0; + result += std::string(firstLine); + delete[] firstLine; + } + else + { + char firstLine[maxLengthToPrint+1]; + strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3); + firstLine[maxLengthToPrint-3] = '.'; + firstLine[maxLengthToPrint-2] = '.'; + firstLine[maxLengthToPrint-1] = '.'; + firstLine[maxLengthToPrint] = 0; + result += std::string(firstLine); + } + + return result; +} + + + + + + + + +// -------- Class SipResponseFirstLine ----------------- + +int SipResponseFirstLine::getStatusCodeAsInt() const +{ + return StatusCodeEnumToInt[m_StatusCode]; +} + +std::string SipResponseFirstLine::getStatusCodeString() const +{ + std::string result; + const int statusStringOffset = 12; + if (m_StatusCode != SipResponseLayer::SipStatusCodeUnknown) + { + int statusStringEndOffset = m_FirstLineEndOffset - 2; + if ((*(m_SipResponse->m_Data + statusStringEndOffset)) != '\r') + statusStringEndOffset++; + result.assign((char*)(m_SipResponse->m_Data + statusStringOffset), statusStringEndOffset-statusStringOffset); + } + + //else first line is illegal, return empty string + + return result; +} + +bool SipResponseFirstLine::setStatusCode(SipResponseLayer::SipResponseStatusCode newStatusCode, std::string statusCodeString) +{ + if (newStatusCode == SipResponseLayer::SipStatusCodeUnknown) + { + PCPP_LOG_ERROR("Requested status code is SipStatusCodeUnknown"); + return false; + } + + //extend or shorten layer + + size_t statusStringOffset = 12; + if (statusCodeString == "") + statusCodeString = StatusCodeEnumToString[newStatusCode]; + int lengthDifference = statusCodeString.length() - getStatusCodeString().length(); + + if (lengthDifference > 0) + { + if (!m_SipResponse->extendLayer(statusStringOffset, lengthDifference)) + { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + } + } + else if (lengthDifference < 0) + { + if (!m_SipResponse->shortenLayer(statusStringOffset, 0-lengthDifference)) + { + PCPP_LOG_ERROR("Cannot change layer size"); + return false; + + } + } + + if (lengthDifference != 0) + { + m_SipResponse->shiftFieldsOffset(m_SipResponse->getFirstField(), lengthDifference); + m_SipResponse->m_FieldsOffset += lengthDifference; + } + + // copy status string + memcpy(m_SipResponse->m_Data+statusStringOffset, statusCodeString.c_str(), statusCodeString.length()); + + // change status code + std::ostringstream statusCodeAsString; + statusCodeAsString << StatusCodeEnumToInt[newStatusCode]; + memcpy(m_SipResponse->m_Data+8, statusCodeAsString.str().c_str(), 3); + + m_StatusCode = newStatusCode; + m_FirstLineEndOffset += lengthDifference; + + return true; + +} + +void SipResponseFirstLine::setVersion(const std::string& newVersion) +{ + if (newVersion == "") + return; + + if (newVersion.length() != m_Version.length()) + { + PCPP_LOG_ERROR("Expected version length is " << m_Version.length() << " characters in the format of SIP/x.y"); + return; + } + + char* verPos = (char*)m_SipResponse->m_Data; + memcpy(verPos, newVersion.c_str(), newVersion.length()); + m_Version = newVersion; +} + +SipResponseLayer::SipResponseStatusCode SipResponseFirstLine::validateStatusCode(char* data, size_t dataLen, SipResponseLayer::SipResponseStatusCode potentialCode) +{ + if (data[0] != ' ') + return SipResponseLayer::SipStatusCodeUnknown; + + return potentialCode; +} + +SipResponseLayer::SipResponseStatusCode SipResponseFirstLine::parseStatusCode(char* data, size_t dataLen) +{ + // minimum data should be 12B long: "SIP/x.y XXX" + if (dataLen < 12) + return SipResponseLayer::SipStatusCodeUnknown; + + char* statusCodeData = data + 8; + size_t statusCodeDataLen = dataLen - 8; + + switch (statusCodeData[0]) + { + case '1': + switch (statusCodeData[1]) + { + case '0': + if (statusCodeData[2] == '0') + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip100Trying); + else + return SipResponseLayer::SipStatusCodeUnknown; + + break; + case '8': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip180Ringing); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip181CallisBeingForwarded); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip182Queued); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip183SessioninProgress); + default: + return SipResponseLayer::SipStatusCodeUnknown; + }; + break; + + case '9': + if (statusCodeData[2] == '9') + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip199EarlyDialogTerminated); + else + return SipResponseLayer::SipStatusCodeUnknown; + break; + + default: + return SipResponseLayer::SipStatusCodeUnknown; + }; + + break; + case '2': + if (statusCodeData[1] == '0') + { + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip200OK); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip202Accepted); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip204NoNotification); + default: + return SipResponseLayer::SipStatusCodeUnknown; + + }; + } + else + return SipResponseLayer::SipStatusCodeUnknown; + + break; + + case '3': + switch (statusCodeData[1]) + { + case '0': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip300MultipleChoices); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip301MovedPermanently); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip302MovedTemporarily); + case '5': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip305UseProxy); + default: + return SipResponseLayer::SipStatusCodeUnknown; + + }; + + break; + + case '8': + if (statusCodeData[2] == '0') + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip380AlternativeService); + else + return SipResponseLayer::SipStatusCodeUnknown; + + break; + + default: + return SipResponseLayer::SipStatusCodeUnknown; + }; + + break; + + case '4': + switch (statusCodeData[1]) + { + case '0': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip400BadRequest); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip401Unauthorized); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip402PaymentRequired); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip403Forbidden); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip404NotFound); + case '5': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip405MethodNotAllowed); + case '6': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip406NotAcceptable); + case '7': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip407ProxyAuthenticationRequired); + case '8': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip408RequestTimeout); + case '9': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip409Conflict); + default: + return SipResponseLayer::SipStatusCodeUnknown; + + }; + + break; + + case '1': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip410Gone); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip411LengthRequired); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip412ConditionalRequestFailed); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip413RequestEntityTooLarge); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip414RequestURITooLong); + case '5': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip415UnsupportedMediaType); + case '6': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip416UnsupportedURIScheme); + case '7': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip417UnknownResourcePriority); + case '8': + default: + return SipResponseLayer::SipStatusCodeUnknown; + + }; + + break; + + case '2': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip420BadExtension); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip421ExtensionRequired); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip422SessionIntervalTooSmall); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip423IntervalTooBrief); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip424BadLocationInformation); + case '8': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip428UseIdentityHeader); + case '9': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip429ProvideReferrerIdentity); + default: + return SipResponseLayer::SipStatusCodeUnknown; + + }; + + break; + + case '3': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip430FlowFailed); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip433AnonymityDisallowed); + case '6': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip436BadIdentityInfo); + case '7': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip437UnsupportedCertificate); + case '8': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip438InvalidIdentityHeader); + case '9': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip439FirstHopLacksOutboundSupport); + default: + return SipResponseLayer::SipStatusCodeUnknown; + }; + + break; + + case '4': + if (statusCodeData[2] == '0') + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip440MaxBreadthExceeded); + else + return SipResponseLayer::SipStatusCodeUnknown; + + break; + + case '6': + if (statusCodeData[2] == '9') + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip469BadInfoPackage); + else + return SipResponseLayer::SipStatusCodeUnknown; + + break; + + case '8': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip480TemporarilyUnavailable); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip481Call_TransactionDoesNotExist); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip482LoopDetected); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip483TooManyHops); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip484AddressIncomplete); + case '5': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip485Ambiguous); + case '6': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip486BusyHere); + case '7': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip487RequestTerminated); + case '8': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip488NotAcceptableHere); + case '9': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip489BadEvent); + default: + return SipResponseLayer::SipStatusCodeUnknown; + }; + + break; + + case '9': + switch (statusCodeData[2]) + { + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip491RequestPending); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip493Undecipherable); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip494SecurityAgreementRequired); + default: + return SipResponseLayer::SipStatusCodeUnknown; + }; + + break; + + default: + return SipResponseLayer::SipStatusCodeUnknown; + }; + + break; + + case '5': + switch (statusCodeData[1]) + { + case '0': + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip500ServerInternalError); + case '1': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip501NotImplemented); + case '2': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip502BadGateway); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip503ServiceUnavailable); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip504ServerTimeout); + case '5': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip505VersionNotSupported); + default: + return SipResponseLayer::SipStatusCodeUnknown; + + }; + + break; + + case '1': + if (statusCodeData[2] == '3') + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip513MessageTooLarge); + else + return SipResponseLayer::SipStatusCodeUnknown; + + break; + + case '8': + if (statusCodeData[2] == '0') + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip580PreconditionFailure); + else + return SipResponseLayer::SipStatusCodeUnknown; + + break; + + default: + return SipResponseLayer::SipStatusCodeUnknown; + }; + + break; + + case '6': + if (statusCodeData[1] == '0') + { + switch (statusCodeData[2]) + { + case '0': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip600BusyEverywhere); + case '3': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip603Decline); + case '4': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip604DoesNotExistAnywhere); + case '6': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip606NotAcceptable); + case '7': + return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, SipResponseLayer::Sip607Unwanted); + default: + return SipResponseLayer::SipStatusCodeUnknown; + }; + } + else + return SipResponseLayer::SipStatusCodeUnknown; + + break; + + default: + return SipResponseLayer::SipStatusCodeUnknown; + }; + + return SipResponseLayer::SipStatusCodeUnknown; +} + +SipResponseFirstLine::SipResponseFirstLine(SipResponseLayer* sipResponse) : m_SipResponse(sipResponse) +{ + m_Version = parseVersion((char*)m_SipResponse->m_Data, m_SipResponse->getDataLen()); + if (m_Version == "") + { + m_StatusCode = SipResponseLayer::SipStatusCodeUnknown; + } + else + { + m_StatusCode = parseStatusCode((char*)m_SipResponse->m_Data, m_SipResponse->getDataLen()); + } + + + char* endOfFirstLine; + if ((endOfFirstLine = (char *)memchr((char*)(m_SipResponse->m_Data), '\n', m_SipResponse->m_DataLen)) != nullptr) + { + m_FirstLineEndOffset = endOfFirstLine - (char*)m_SipResponse->m_Data + 1; + m_IsComplete = true; + } + else + { + m_FirstLineEndOffset = m_SipResponse->getDataLen(); + m_IsComplete = false; + } + + if (Logger::getInstance().isDebugEnabled(PacketLogModuleSipLayer)) + { + int statusCode = (m_StatusCode == SipResponseLayer::SipStatusCodeUnknown ? 0 : StatusCodeEnumToInt[m_StatusCode]); + PCPP_LOG_DEBUG("Version='" << m_Version << "'; Status code=" << statusCode << " '" << getStatusCodeString() << "'"); + } +} + + +SipResponseFirstLine::SipResponseFirstLine(SipResponseLayer* sipResponse, const std::string& version, SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString) +{ + if (statusCode == SipResponseLayer::SipStatusCodeUnknown) + { + m_Exception.setMessage("Status code supplied was SipStatusCodeUnknown"); + throw m_Exception; + } + + if (version == "") + { + m_Exception.setMessage("Version supplied was unknown"); + throw m_Exception; + } + + m_SipResponse = sipResponse; + + m_StatusCode = statusCode; + m_Version = version; + + std::ostringstream statusCodeAsString; + statusCodeAsString << StatusCodeEnumToInt[m_StatusCode]; + if (statusCodeString == "") + statusCodeString = StatusCodeEnumToString[m_StatusCode]; + std::string firstLine = m_Version + " " + statusCodeAsString.str() + " " + statusCodeString + "\r\n"; + + m_FirstLineEndOffset = firstLine.length(); + + m_SipResponse->m_DataLen = firstLine.length(); + m_SipResponse->m_Data = new uint8_t[m_SipResponse->m_DataLen]; + memcpy(m_SipResponse->m_Data, firstLine.c_str(), m_SipResponse->m_DataLen); + + m_IsComplete = true; +} + +std::string SipResponseFirstLine::parseVersion(char* data, size_t dataLen) +{ + if (dataLen < 7) // "SIP/x.y" + { + PCPP_LOG_DEBUG("SIP response length < 7, cannot identify version"); + return ""; + } + + if (data[0] != 'S' || data[1] != 'I' || data[2] != 'P' || data[3] != '/') + { + PCPP_LOG_DEBUG("SIP response does not begin with 'SIP/'"); + return ""; + } + + char* nextSpace = (char*)memchr(data, ' ', dataLen); + if (nextSpace == nullptr) + return ""; + + return std::string(data, nextSpace - data); +} + + +} diff --git a/Packet++/src/SomeIpLayer.cpp b/Packet++/src/SomeIpLayer.cpp index 797af78734..bcf002e624 100644 --- a/Packet++/src/SomeIpLayer.cpp +++ b/Packet++/src/SomeIpLayer.cpp @@ -1,366 +1,366 @@ -#define LOG_MODULE PacketLogModuleSomeIpLayer - -#include "SomeIpLayer.h" -#include "SomeIpSdLayer.h" -#include "Packet.h" -#include "PayloadLayer.h" -#include "EndianPortable.h" -#include -#include -#include - -namespace pcpp -{ - -// SomeIpLayer - -void splitUint32Id(uint32_t uint32Id, uint16_t &uint16IdUpper, uint16_t &uint16IdLower) -{ - uint16IdLower = (uint32Id & 0x0000ffff); - uint16IdUpper = (uint32Id & 0xffff0000) >> 16; -} - -std::unordered_set SomeIpLayer::m_SomeIpPorts{}; - -SomeIpLayer::SomeIpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, - uint8_t interfaceVersion, MsgType type, uint8_t returnCode, const uint8_t *const data, - size_t dataLen) -{ - const size_t headerLen = sizeof(someiphdr); - m_DataLen = headerLen + dataLen; - m_Data = new uint8_t[m_DataLen]; - m_Protocol = SomeIP; - memset(m_Data, 0, headerLen); - memcpy(m_Data + headerLen, data, dataLen); - - setServiceID(serviceID); - setMethodID(methodID); - setPayloadLength((uint32_t)dataLen); - setClientID(clientID); - setSessionID(sessionID); - setProtocolVersion(0x01); - setInterfaceVersion(interfaceVersion); - setMessageType(type); - setReturnCode(returnCode); -} - -Layer* SomeIpLayer::parseSomeIpLayer(uint8_t *data, size_t dataLen, Layer* prevLayer, Packet* packet) -{ - /* Ideas taken from wireshark some ip dissector */ - const size_t headerLen = sizeof(someiphdr); - if (dataLen < headerLen) - return new PayloadLayer(data, dataLen, prevLayer, packet); - - uint32_t lengthBE = 0; - memcpy(&lengthBE, data + sizeof(uint32_t), sizeof(uint32_t)); // length field in SOME/IP header - uint32_t length = be32toh(lengthBE); - if ((length < 8) || (length > dataLen - 8)) - return new PayloadLayer(data, dataLen, prevLayer, packet); - - if (data[12] != SOMEIP_PROTOCOL_VERSION) - return new PayloadLayer(data, dataLen, prevLayer, packet); - - someiphdr *hdr = (someiphdr *)data; - - switch (static_cast(hdr->msgType & ~(uint8_t)MsgType::TP_REQUEST)) - { - case MsgType::REQUEST: - case MsgType::REQUEST_ACK: - case MsgType::REQUEST_NO_RETURN: - case MsgType::REQUEST_NO_RETURN_ACK: - case MsgType::NOTIFICATION: - case MsgType::NOTIFICATION_ACK: - case MsgType::RESPONSE: - case MsgType::RESPONSE_ACK: - case MsgType::ERRORS: - case MsgType::ERROR_ACK: - break; - default: - return new PayloadLayer(data, dataLen, prevLayer, packet); - } - - if (be16toh(hdr->serviceID) == 0xFFFF && be16toh(hdr->methodID) == 0x8100) - { - return new SomeIpSdLayer(data, dataLen, prevLayer, packet); - } - else if ((hdr->msgType & (uint8_t)SomeIpLayer::MsgType::TP_REQUEST) != 0) - { - return new SomeIpTpLayer(data, dataLen, prevLayer, packet); - } - else - { - return new SomeIpLayer(data, dataLen, prevLayer, packet); - } -} - -bool SomeIpLayer::isSomeIpPort(uint16_t port) -{ - return SomeIpSdLayer::isSomeIpSdPort(port) || - std::any_of(m_SomeIpPorts.begin(), m_SomeIpPorts.end(), - [&](const uint16_t &someIpPort) { return someIpPort == port; }); -} - -void SomeIpLayer::addSomeIpPort(uint16_t port) -{ - m_SomeIpPorts.insert(port); -} - -void SomeIpLayer::removeSomeIpPort(uint16_t port) -{ - m_SomeIpPorts.erase(port); -} - -void SomeIpLayer::removeAllSomeIpPorts() -{ - m_SomeIpPorts.clear(); -} - -uint32_t SomeIpLayer::getMessageID() const -{ - someiphdr *hdr = getSomeIpHeader(); - - return ((uint32_t)be16toh(hdr->serviceID) << 16) + be16toh(hdr->methodID); -} - -void SomeIpLayer::setMessageID(uint32_t messageID) -{ - uint16_t methodID; - uint16_t serviceID; - - splitUint32Id(messageID, serviceID, methodID); - - someiphdr *hdr = getSomeIpHeader(); - hdr->serviceID = htobe16(serviceID); - hdr->methodID = htobe16(methodID); -} - -uint16_t SomeIpLayer::getServiceID() const -{ - return be16toh(getSomeIpHeader()->serviceID); -} - -void SomeIpLayer::setServiceID(uint16_t serviceID) -{ - getSomeIpHeader()->serviceID = htobe16(serviceID); -} - -uint16_t SomeIpLayer::getMethodID() const -{ - return be16toh(getSomeIpHeader()->methodID); -} - -void SomeIpLayer::setMethodID(uint16_t methodID) -{ - getSomeIpHeader()->methodID = htobe16(methodID); -} - -uint32_t SomeIpLayer::getLengthField() const -{ - return be32toh(getSomeIpHeader()->length); -} - -uint32_t SomeIpLayer::getRequestID() const -{ - someiphdr *hdr = getSomeIpHeader(); - - return ((uint32_t)be16toh(hdr->clientID) << 16) + be16toh(hdr->sessionID); -} - -void SomeIpLayer::setRequestID(uint32_t requestID) -{ - uint16_t clientID; - uint16_t sessionID; - - splitUint32Id(requestID, clientID, sessionID); - - someiphdr *hdr = getSomeIpHeader(); - hdr->clientID = htobe16(clientID); - hdr->sessionID = htobe16(sessionID); -} - -uint16_t SomeIpLayer::getClientID() const -{ - return be16toh(getSomeIpHeader()->clientID); -} - -void SomeIpLayer::setClientID(uint16_t clientID) -{ - getSomeIpHeader()->clientID = htobe16(clientID); -} - -uint16_t SomeIpLayer::getSessionID() const -{ - return be16toh(getSomeIpHeader()->sessionID); -} - -void SomeIpLayer::setSessionID(uint16_t sessionID) -{ - getSomeIpHeader()->sessionID = htobe16(sessionID); -} - -uint8_t SomeIpLayer::getProtocolVersion() const -{ - return getSomeIpHeader()->protocolVersion; -} - -void SomeIpLayer::setProtocolVersion(uint8_t version) -{ - getSomeIpHeader()->protocolVersion = version; -} - -uint8_t SomeIpLayer::getInterfaceVersion() const -{ - return getSomeIpHeader()->interfaceVersion; -} - -void SomeIpLayer::setInterfaceVersion(uint8_t version) -{ - getSomeIpHeader()->interfaceVersion = version; -} - -SomeIpLayer::MsgType SomeIpLayer::getMessageType() const -{ - return static_cast(getSomeIpHeader()->msgType); -} - -uint8_t SomeIpLayer::getMessageTypeAsInt() const -{ - return getSomeIpHeader()->msgType; -} - -void SomeIpLayer::setMessageType(MsgType type) -{ - setMessageType(static_cast(type)); -} - -void SomeIpLayer::setMessageType(uint8_t type) -{ - getSomeIpHeader()->msgType = type; -} - -uint8_t SomeIpLayer::getReturnCode() const -{ - return getSomeIpHeader()->returnCode; -} - -void SomeIpLayer::setReturnCode(uint8_t returnCode) -{ - getSomeIpHeader()->returnCode = returnCode; -} - -void SomeIpLayer::setPayloadLength(uint32_t payloadLength) -{ - someiphdr *hdr = getSomeIpHeader(); - hdr->length = htobe32(sizeof(someiphdr) - sizeof(hdr->serviceID) - sizeof(hdr->methodID) - sizeof(hdr->length) + - payloadLength); -} - -void SomeIpLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; - - uint8_t *payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; - - m_NextLayer = parseSomeIpLayer(payload, payloadLen, this, m_Packet); -} - -std::string SomeIpLayer::toString() const -{ - std::stringstream dataStream; - - dataStream << "SOME/IP Layer" - << std::hex - << ", Service ID: 0x" << getServiceID() - << ", Method ID: 0x" << getMethodID() - << std::dec - << ", Length: " << getLengthField(); - - return dataStream.str(); -} - -// SomeIpTpLayer - -SomeIpTpLayer::SomeIpTpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, - uint8_t interfaceVersion, MsgType type, uint8_t returnCode, uint32_t offset, - bool moreSegmentsFlag, const uint8_t *const data, size_t dataLen) -{ - const size_t headerLen = sizeof(someiptphdr); - - m_DataLen = headerLen + dataLen; - m_Data = new uint8_t[m_DataLen]; - m_Protocol = SomeIP; - memset(m_Data, 0, headerLen); - memcpy(m_Data + headerLen, data, dataLen); - - setServiceID(serviceID); - setMethodID(methodID); - setPayloadLength((uint32_t)(dataLen + sizeof(uint32_t))); - setClientID(clientID); - setSessionID(sessionID); - setProtocolVersion(0x01); - setInterfaceVersion(interfaceVersion); - setMessageType(setTpFlag((uint8_t)type)); - setReturnCode(returnCode); - setOffset(offset); - setMoreSegmentsFlag(moreSegmentsFlag); -} - -uint32_t SomeIpTpLayer::getOffset() const -{ - return (be32toh(getSomeIpTpHeader()->offsetAndFlag) & SOMEIP_TP_OFFSET_MASK) >> 4; -} - -void SomeIpTpLayer::setOffset(uint32_t offset) -{ - uint32_t val = (offset << 4) | (be32toh(getSomeIpTpHeader()->offsetAndFlag) & ~SOMEIP_TP_OFFSET_MASK); - getSomeIpTpHeader()->offsetAndFlag = htobe32(val); -} - -bool SomeIpTpLayer::getMoreSegmentsFlag() const -{ - return be32toh(getSomeIpTpHeader()->offsetAndFlag) & SOMEIP_TP_MORE_FLAG_MASK; -} - -void SomeIpTpLayer::setMoreSegmentsFlag(bool flag) -{ - uint32_t val = be32toh(getSomeIpTpHeader()->offsetAndFlag); - - if (flag) - { - val = val | SOMEIP_TP_MORE_FLAG_MASK; - } - else - { - val = val & ~SOMEIP_TP_MORE_FLAG_MASK; - } - - getSomeIpTpHeader()->offsetAndFlag = htobe32(val); -} - -void SomeIpTpLayer::computeCalculateFields() -{ - setMessageType(setTpFlag(getMessageTypeAsInt())); -} - -std::string SomeIpTpLayer::toString() const -{ - std::stringstream dataStream; - - dataStream << "SOME/IP-TP Layer" - << std::hex - << ", Service ID: 0x" << getServiceID() - << ", Method ID: 0x" << getMethodID() - << std::dec - << ", Length: " << getLengthField(); - - return dataStream.str(); -} - -uint8_t SomeIpTpLayer::setTpFlag(uint8_t messageType) -{ - return messageType | (uint8_t)SomeIpLayer::MsgType::TP_REQUEST; -} -} // namespace pcpp +#define LOG_MODULE PacketLogModuleSomeIpLayer + +#include "SomeIpLayer.h" +#include "SomeIpSdLayer.h" +#include "Packet.h" +#include "PayloadLayer.h" +#include "EndianPortable.h" +#include +#include +#include + +namespace pcpp +{ + +// SomeIpLayer + +void splitUint32Id(uint32_t uint32Id, uint16_t &uint16IdUpper, uint16_t &uint16IdLower) +{ + uint16IdLower = (uint32Id & 0x0000ffff); + uint16IdUpper = (uint32Id & 0xffff0000) >> 16; +} + +std::unordered_set SomeIpLayer::m_SomeIpPorts{}; + +SomeIpLayer::SomeIpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, + uint8_t interfaceVersion, MsgType type, uint8_t returnCode, const uint8_t *const data, + size_t dataLen) +{ + const size_t headerLen = sizeof(someiphdr); + m_DataLen = headerLen + dataLen; + m_Data = new uint8_t[m_DataLen]; + m_Protocol = SomeIP; + memset(m_Data, 0, headerLen); + memcpy(m_Data + headerLen, data, dataLen); + + setServiceID(serviceID); + setMethodID(methodID); + setPayloadLength((uint32_t)dataLen); + setClientID(clientID); + setSessionID(sessionID); + setProtocolVersion(0x01); + setInterfaceVersion(interfaceVersion); + setMessageType(type); + setReturnCode(returnCode); +} + +Layer* SomeIpLayer::parseSomeIpLayer(uint8_t *data, size_t dataLen, Layer* prevLayer, Packet* packet) +{ + /* Ideas taken from wireshark some ip dissector */ + const size_t headerLen = sizeof(someiphdr); + if (dataLen < headerLen) + return new PayloadLayer(data, dataLen, prevLayer, packet); + + uint32_t lengthBE = 0; + memcpy(&lengthBE, data + sizeof(uint32_t), sizeof(uint32_t)); // length field in SOME/IP header + uint32_t length = be32toh(lengthBE); + if ((length < 8) || (length > dataLen - 8)) + return new PayloadLayer(data, dataLen, prevLayer, packet); + + if (data[12] != SOMEIP_PROTOCOL_VERSION) + return new PayloadLayer(data, dataLen, prevLayer, packet); + + someiphdr *hdr = (someiphdr *)data; + + switch (static_cast(hdr->msgType & ~(uint8_t)MsgType::TP_REQUEST)) + { + case MsgType::REQUEST: + case MsgType::REQUEST_ACK: + case MsgType::REQUEST_NO_RETURN: + case MsgType::REQUEST_NO_RETURN_ACK: + case MsgType::NOTIFICATION: + case MsgType::NOTIFICATION_ACK: + case MsgType::RESPONSE: + case MsgType::RESPONSE_ACK: + case MsgType::ERRORS: + case MsgType::ERROR_ACK: + break; + default: + return new PayloadLayer(data, dataLen, prevLayer, packet); + } + + if (be16toh(hdr->serviceID) == 0xFFFF && be16toh(hdr->methodID) == 0x8100) + { + return new SomeIpSdLayer(data, dataLen, prevLayer, packet); + } + else if ((hdr->msgType & (uint8_t)SomeIpLayer::MsgType::TP_REQUEST) != 0) + { + return new SomeIpTpLayer(data, dataLen, prevLayer, packet); + } + else + { + return new SomeIpLayer(data, dataLen, prevLayer, packet); + } +} + +bool SomeIpLayer::isSomeIpPort(uint16_t port) +{ + return SomeIpSdLayer::isSomeIpSdPort(port) || + std::any_of(m_SomeIpPorts.begin(), m_SomeIpPorts.end(), + [&](const uint16_t &someIpPort) { return someIpPort == port; }); +} + +void SomeIpLayer::addSomeIpPort(uint16_t port) +{ + m_SomeIpPorts.insert(port); +} + +void SomeIpLayer::removeSomeIpPort(uint16_t port) +{ + m_SomeIpPorts.erase(port); +} + +void SomeIpLayer::removeAllSomeIpPorts() +{ + m_SomeIpPorts.clear(); +} + +uint32_t SomeIpLayer::getMessageID() const +{ + someiphdr *hdr = getSomeIpHeader(); + + return ((uint32_t)be16toh(hdr->serviceID) << 16) + be16toh(hdr->methodID); +} + +void SomeIpLayer::setMessageID(uint32_t messageID) +{ + uint16_t methodID; + uint16_t serviceID; + + splitUint32Id(messageID, serviceID, methodID); + + someiphdr *hdr = getSomeIpHeader(); + hdr->serviceID = htobe16(serviceID); + hdr->methodID = htobe16(methodID); +} + +uint16_t SomeIpLayer::getServiceID() const +{ + return be16toh(getSomeIpHeader()->serviceID); +} + +void SomeIpLayer::setServiceID(uint16_t serviceID) +{ + getSomeIpHeader()->serviceID = htobe16(serviceID); +} + +uint16_t SomeIpLayer::getMethodID() const +{ + return be16toh(getSomeIpHeader()->methodID); +} + +void SomeIpLayer::setMethodID(uint16_t methodID) +{ + getSomeIpHeader()->methodID = htobe16(methodID); +} + +uint32_t SomeIpLayer::getLengthField() const +{ + return be32toh(getSomeIpHeader()->length); +} + +uint32_t SomeIpLayer::getRequestID() const +{ + someiphdr *hdr = getSomeIpHeader(); + + return ((uint32_t)be16toh(hdr->clientID) << 16) + be16toh(hdr->sessionID); +} + +void SomeIpLayer::setRequestID(uint32_t requestID) +{ + uint16_t clientID; + uint16_t sessionID; + + splitUint32Id(requestID, clientID, sessionID); + + someiphdr *hdr = getSomeIpHeader(); + hdr->clientID = htobe16(clientID); + hdr->sessionID = htobe16(sessionID); +} + +uint16_t SomeIpLayer::getClientID() const +{ + return be16toh(getSomeIpHeader()->clientID); +} + +void SomeIpLayer::setClientID(uint16_t clientID) +{ + getSomeIpHeader()->clientID = htobe16(clientID); +} + +uint16_t SomeIpLayer::getSessionID() const +{ + return be16toh(getSomeIpHeader()->sessionID); +} + +void SomeIpLayer::setSessionID(uint16_t sessionID) +{ + getSomeIpHeader()->sessionID = htobe16(sessionID); +} + +uint8_t SomeIpLayer::getProtocolVersion() const +{ + return getSomeIpHeader()->protocolVersion; +} + +void SomeIpLayer::setProtocolVersion(uint8_t version) +{ + getSomeIpHeader()->protocolVersion = version; +} + +uint8_t SomeIpLayer::getInterfaceVersion() const +{ + return getSomeIpHeader()->interfaceVersion; +} + +void SomeIpLayer::setInterfaceVersion(uint8_t version) +{ + getSomeIpHeader()->interfaceVersion = version; +} + +SomeIpLayer::MsgType SomeIpLayer::getMessageType() const +{ + return static_cast(getSomeIpHeader()->msgType); +} + +uint8_t SomeIpLayer::getMessageTypeAsInt() const +{ + return getSomeIpHeader()->msgType; +} + +void SomeIpLayer::setMessageType(MsgType type) +{ + setMessageType(static_cast(type)); +} + +void SomeIpLayer::setMessageType(uint8_t type) +{ + getSomeIpHeader()->msgType = type; +} + +uint8_t SomeIpLayer::getReturnCode() const +{ + return getSomeIpHeader()->returnCode; +} + +void SomeIpLayer::setReturnCode(uint8_t returnCode) +{ + getSomeIpHeader()->returnCode = returnCode; +} + +void SomeIpLayer::setPayloadLength(uint32_t payloadLength) +{ + someiphdr *hdr = getSomeIpHeader(); + hdr->length = htobe32(sizeof(someiphdr) - sizeof(hdr->serviceID) - sizeof(hdr->methodID) - sizeof(hdr->length) + + payloadLength); +} + +void SomeIpLayer::parseNextLayer() +{ + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; + + uint8_t *payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; + + m_NextLayer = parseSomeIpLayer(payload, payloadLen, this, m_Packet); +} + +std::string SomeIpLayer::toString() const +{ + std::stringstream dataStream; + + dataStream << "SOME/IP Layer" + << std::hex + << ", Service ID: 0x" << getServiceID() + << ", Method ID: 0x" << getMethodID() + << std::dec + << ", Length: " << getLengthField(); + + return dataStream.str(); +} + +// SomeIpTpLayer + +SomeIpTpLayer::SomeIpTpLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, + uint8_t interfaceVersion, MsgType type, uint8_t returnCode, uint32_t offset, + bool moreSegmentsFlag, const uint8_t *const data, size_t dataLen) +{ + const size_t headerLen = sizeof(someiptphdr); + + m_DataLen = headerLen + dataLen; + m_Data = new uint8_t[m_DataLen]; + m_Protocol = SomeIP; + memset(m_Data, 0, headerLen); + memcpy(m_Data + headerLen, data, dataLen); + + setServiceID(serviceID); + setMethodID(methodID); + setPayloadLength((uint32_t)(dataLen + sizeof(uint32_t))); + setClientID(clientID); + setSessionID(sessionID); + setProtocolVersion(0x01); + setInterfaceVersion(interfaceVersion); + setMessageType(setTpFlag((uint8_t)type)); + setReturnCode(returnCode); + setOffset(offset); + setMoreSegmentsFlag(moreSegmentsFlag); +} + +uint32_t SomeIpTpLayer::getOffset() const +{ + return (be32toh(getSomeIpTpHeader()->offsetAndFlag) & SOMEIP_TP_OFFSET_MASK) >> 4; +} + +void SomeIpTpLayer::setOffset(uint32_t offset) +{ + uint32_t val = (offset << 4) | (be32toh(getSomeIpTpHeader()->offsetAndFlag) & ~SOMEIP_TP_OFFSET_MASK); + getSomeIpTpHeader()->offsetAndFlag = htobe32(val); +} + +bool SomeIpTpLayer::getMoreSegmentsFlag() const +{ + return be32toh(getSomeIpTpHeader()->offsetAndFlag) & SOMEIP_TP_MORE_FLAG_MASK; +} + +void SomeIpTpLayer::setMoreSegmentsFlag(bool flag) +{ + uint32_t val = be32toh(getSomeIpTpHeader()->offsetAndFlag); + + if (flag) + { + val = val | SOMEIP_TP_MORE_FLAG_MASK; + } + else + { + val = val & ~SOMEIP_TP_MORE_FLAG_MASK; + } + + getSomeIpTpHeader()->offsetAndFlag = htobe32(val); +} + +void SomeIpTpLayer::computeCalculateFields() +{ + setMessageType(setTpFlag(getMessageTypeAsInt())); +} + +std::string SomeIpTpLayer::toString() const +{ + std::stringstream dataStream; + + dataStream << "SOME/IP-TP Layer" + << std::hex + << ", Service ID: 0x" << getServiceID() + << ", Method ID: 0x" << getMethodID() + << std::dec + << ", Length: " << getLengthField(); + + return dataStream.str(); +} + +uint8_t SomeIpTpLayer::setTpFlag(uint8_t messageType) +{ + return messageType | (uint8_t)SomeIpLayer::MsgType::TP_REQUEST; +} +} // namespace pcpp diff --git a/Packet++/src/SomeIpSdLayer.cpp b/Packet++/src/SomeIpSdLayer.cpp index 6a0400c46c..764b069617 100644 --- a/Packet++/src/SomeIpSdLayer.cpp +++ b/Packet++/src/SomeIpSdLayer.cpp @@ -1,789 +1,789 @@ -#define LOG_MODULE PacketLogModuleSomeIpSdLayer - -#include "SomeIpSdLayer.h" -#include -#include -#include -#include - -namespace pcpp -{ - -/* - * SomeIpSdOption - */ -SomeIpSdOption::~SomeIpSdOption() -{ - if (m_ShadowData != nullptr) - delete[] m_ShadowData; -} - -SomeIpSdOption::OptionType SomeIpSdOption::getType() const { - return static_cast(getSomeIpSdOptionHeader()->type); -} - -uint8_t* SomeIpSdOption::getDataPtr() const -{ - if (m_DataContainer != nullptr) - return m_DataContainer->getDataPtr(m_Offset); - - return m_ShadowData; -} - -SomeIpSdOption::someipsdhdroptionsbase *SomeIpSdOption::getSomeIpSdOptionHeader() const -{ - return (someipsdhdroptionsbase *)getDataPtr(); -} - -void SomeIpSdOption::initStdFields(OptionType type) -{ - someipsdhdroptionsbase *optionHdr = getSomeIpSdOptionHeader(); - - optionHdr->type = static_cast(type); - /* Length field is excluding length field itself and uint8_t type field */ - optionHdr->length = htobe16((uint16_t)(m_DataLen - sizeof(optionHdr->length) - sizeof(optionHdr->type))); -} - -/* - * SomeIpSdIPv4Option - */ -SomeIpSdIPv4Option::SomeIpSdIPv4Option(IPv4OptionType type, IPv4Address ipAddress, uint16_t port, - SomeIpSdProtocolType l4Protocol) -{ - m_DataLen = sizeof(someipsdhdroptionsipv4); - m_ShadowData = new uint8_t[m_DataLen]; - memset(m_ShadowData, 0, m_DataLen); - - switch(type) - { - case IPv4OptionType::IPv4Endpoint: - initStdFields(OptionType::IPv4Endpoint); - break; - case IPv4OptionType::IPv4Multicast: - initStdFields(OptionType::IPv4Multicast); - break; - case IPv4OptionType::IPv4SdEndpoint: - initStdFields(OptionType::IPv4SdEndpoint); - break; - } - - someipsdhdroptionsipv4 *hdr = (someipsdhdroptionsipv4 *)getDataPtr(); - hdr->ipv4Address = ipAddress.toInt(); - hdr->portNumber = htobe16(port); - hdr->l4Protocol = l4Protocol; -} - -SomeIpSdIPv4Option::SomeIpSdIPv4Option(const IDataContainer *dataContainer, size_t offset) - : SomeIpSdOption(dataContainer, offset) -{ - m_DataLen = sizeof(someipsdhdroptionsipv4); -} - -IPv4Address SomeIpSdIPv4Option::getIpAddress() const -{ - someipsdhdroptionsipv4 *hdr = (someipsdhdroptionsipv4 *)getDataPtr(); - IPv4Address ipAddr(hdr->ipv4Address); - - return ipAddr; -} - -uint16_t SomeIpSdIPv4Option::getPort() const -{ - someipsdhdroptionsipv4 *hdr = (someipsdhdroptionsipv4 *)getDataPtr(); - return be16toh(hdr->portNumber); -} - -SomeIpSdProtocolType SomeIpSdIPv4Option::getProtocol() const -{ - someipsdhdroptionsipv4 *hdr = (someipsdhdroptionsipv4 *)getDataPtr(); - return hdr->l4Protocol; -} - -/* - * SomeIpSdIPv6Option - */ -SomeIpSdIPv6Option::SomeIpSdIPv6Option(IPv6OptionType type, IPv6Address ipAddress, uint16_t port, - SomeIpSdProtocolType l4Protocol) -{ - m_DataLen = sizeof(someipsdhdroptionsipv6); - m_ShadowData = new uint8_t[m_DataLen]; - memset(m_ShadowData, 0, m_DataLen); - - switch(type) - { - case IPv6OptionType::IPv6Endpoint: - initStdFields(OptionType::IPv6Endpoint); - break; - case IPv6OptionType::IPv6Multicast: - initStdFields(OptionType::IPv6Multicast); - break; - case IPv6OptionType::IPv6SdEndpoint: - initStdFields(OptionType::IPv6SdEndpoint); - break; - } - - someipsdhdroptionsipv6 *hdr = (someipsdhdroptionsipv6 *)getDataPtr(); - std::memcpy(hdr->ipv6Address, ipAddress.toBytes(), 16); - hdr->portNumber = htobe16(port); - hdr->l4Protocol = l4Protocol; -} - -SomeIpSdIPv6Option::SomeIpSdIPv6Option(const IDataContainer *dataContainer, size_t offset) - : SomeIpSdOption(dataContainer, offset) -{ - m_DataLen = sizeof(someipsdhdroptionsipv6); -} - -IPv6Address SomeIpSdIPv6Option::getIpAddress() const -{ - someipsdhdroptionsipv6 *hdr = (someipsdhdroptionsipv6 *)getDataPtr(); - IPv6Address ipAddr(hdr->ipv6Address); - - return ipAddr; -} - -uint16_t SomeIpSdIPv6Option::getPort() const -{ - someipsdhdroptionsipv6 *hdr = (someipsdhdroptionsipv6 *)getDataPtr(); - return be16toh(hdr->portNumber); -} - -SomeIpSdProtocolType SomeIpSdIPv6Option::getProtocol() const -{ - someipsdhdroptionsipv6 *hdr = (someipsdhdroptionsipv6 *)getDataPtr(); - return hdr->l4Protocol; -} - -/* - * SomeIpSdConfigurationOption - */ -SomeIpSdConfigurationOption::SomeIpSdConfigurationOption(const std::string &configurationString) -{ - m_DataLen = configurationString.length() + sizeof(someipsdhdroptionsbase); - m_ShadowData = new uint8_t[m_DataLen]; - memset(m_ShadowData, 0, m_DataLen); - - initStdFields(OptionType::ConfigurationString); - std::memcpy(getDataPtr() + sizeof(someipsdhdroptionsbase), configurationString.c_str(), - configurationString.length()); -} - -SomeIpSdConfigurationOption::SomeIpSdConfigurationOption(const IDataContainer *dataContainer, size_t offset) - : SomeIpSdOption(dataContainer, offset) -{ - m_DataLen = sizeof(someipsdhdroptionsbase) - 1 + be16toh(getSomeIpSdOptionHeader()->length); -} - -std::string SomeIpSdConfigurationOption::getConfigurationString() const -{ - return std::string((char *)getDataPtr() + sizeof(someipsdhdroptionsbase), - be16toh(getSomeIpSdOptionHeader()->length) - 1); -} - -/* - * SomeIpSdLoadBalancingOption - */ -SomeIpSdLoadBalancingOption::SomeIpSdLoadBalancingOption(uint16_t priority, uint16_t weight) -{ - m_DataLen = sizeof(someipsdhdroptionsload); - m_ShadowData = new uint8_t[m_DataLen]; - memset(m_ShadowData, 0, m_DataLen); - - initStdFields(OptionType::LoadBalancing); - - someipsdhdroptionsload *hdr = (someipsdhdroptionsload *)getDataPtr(); - hdr->priority = htobe16(priority); - hdr->weight = htobe16(weight); -} - -SomeIpSdLoadBalancingOption::SomeIpSdLoadBalancingOption(const IDataContainer *dataContainer, size_t offset) - : SomeIpSdOption(dataContainer, offset) -{ - m_DataLen = sizeof(someipsdhdroptionsload); -} - -uint16_t SomeIpSdLoadBalancingOption::getPriority() const -{ - someipsdhdroptionsload *hdr = (someipsdhdroptionsload *)getDataPtr(); - return be16toh(hdr->priority); -} - -uint16_t SomeIpSdLoadBalancingOption::getWeight() const -{ - someipsdhdroptionsload *hdr = (someipsdhdroptionsload *)getDataPtr(); - return be16toh(hdr->weight); -} - -/* - * SomeIpSdEntry - */ - -SomeIpSdEntry::SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, - uint32_t TTL, uint32_t minorVersion) -{ - initStdFields(type, serviceID, instanceID, majorVersion, TTL); - setMinorVersion(minorVersion); -} - -SomeIpSdEntry::SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, - uint32_t TTL, uint8_t counter, uint16_t eventGroupID) -{ - initStdFields(type, serviceID, instanceID, majorVersion, TTL); - setCounter(counter); - setEventgroupId(eventGroupID); -} - -SomeIpSdEntry::SomeIpSdEntry(const SomeIpSdLayer *pSomeIpSdLayer, size_t offset) - : m_Layer(pSomeIpSdLayer), m_Offset(offset), m_ShadowData(nullptr) -{ - EntryType entryType; - - someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); - TypeInternal internalType = static_cast(hdr->type); - auto ttl = getTtl(); - - switch(internalType) - { - case SomeIpSdEntry::TypeInternal::FindService_Internal: - entryType = SomeIpSdEntry::EntryType::FindService; - break; - case SomeIpSdEntry::TypeInternal::OfferService_Internal: - if (ttl == 0) - { - entryType = EntryType::StopOfferService; - } - else - { - entryType = EntryType::OfferService; - } - break; - case SomeIpSdEntry::TypeInternal::SubscribeEventgroup_Internal: - if (ttl == 0) - { - entryType = EntryType::StopSubscribeEventgroup; - } - else - { - entryType = EntryType::SubscribeEventgroup; - } - break; - case SomeIpSdEntry::TypeInternal::SubscribeEventgroupAck_Internal: - if (ttl == 0) - { - entryType = EntryType::SubscribeEventgroupNack; - } - else - { - entryType = EntryType::SubscribeEventgroupAck; - } - break; - default: - entryType = EntryType::UnknownEntryType; - break; - } - - m_EntryType = entryType; -} - -SomeIpSdEntry::~SomeIpSdEntry() -{ - if (m_ShadowData != nullptr) - delete[] m_ShadowData; -} - -uint8_t *SomeIpSdEntry::getDataPtr() const -{ - if (m_Layer != nullptr) - return m_Layer->getDataPtr(m_Offset); - - return m_ShadowData; -} - -SomeIpSdEntry::someipsdhdrentry *SomeIpSdEntry::getSomeIpSdEntryHeader() const -{ - return (someipsdhdrentry *)getDataPtr(); -} - -uint32_t SomeIpSdEntry::getNumOptions() const -{ - auto *hdr = getSomeIpSdEntryHeader(); - return hdr->nrOpt1 + hdr->nrOpt2; -} - -uint16_t SomeIpSdEntry::getServiceId() const -{ - return be16toh(getSomeIpSdEntryHeader()->serviceID); -} - -void SomeIpSdEntry::setServiceId(uint16_t serviceId) -{ - getSomeIpSdEntryHeader()->serviceID = htobe16(serviceId); -} - -uint16_t SomeIpSdEntry::getInstanceId() const -{ - return be16toh(getSomeIpSdEntryHeader()->instanceID); -} - -void SomeIpSdEntry::setInstanceId(uint16_t instanceId) -{ - getSomeIpSdEntryHeader()->instanceID = htobe16(instanceId); -} - -uint8_t SomeIpSdEntry::getMajorVersion() const -{ - return (be32toh(getSomeIpSdEntryHeader()->majorVersion_ttl) & ~SOMEIPSD_HDR_ENTRY_MASK_TTL) >> 24; -} - -void SomeIpSdEntry::setMajorVersion(uint8_t majorVersion) -{ - someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); - uint32_t val = (majorVersion << 24) | (be32toh(hdr->majorVersion_ttl) & SOMEIPSD_HDR_ENTRY_MASK_TTL); - hdr->majorVersion_ttl = htobe32(val); -} - -uint32_t SomeIpSdEntry::getTtl() const -{ - return be32toh(getSomeIpSdEntryHeader()->majorVersion_ttl) & SOMEIPSD_HDR_ENTRY_MASK_TTL; -} - -void SomeIpSdEntry::setTtl(uint32_t ttl) -{ - someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); - uint32_t val = (ttl & SOMEIPSD_HDR_ENTRY_MASK_TTL) | (be32toh(hdr->majorVersion_ttl) & ~SOMEIPSD_HDR_ENTRY_MASK_TTL); - hdr->majorVersion_ttl = htobe32(val); -} - -uint32_t SomeIpSdEntry::getMinorVersion() const -{ - return be32toh(getSomeIpSdEntryHeader()->data); -} - -void SomeIpSdEntry::setMinorVersion(uint32_t minorVersion) -{ - getSomeIpSdEntryHeader()->data = htobe32(minorVersion); -} - -uint8_t SomeIpSdEntry::getCounter() const -{ - return (uint8_t)((be32toh(getSomeIpSdEntryHeader()->data) >> 16) & 0x0F); -} - -void SomeIpSdEntry::setCounter(uint8_t counter) -{ - someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); - hdr->data = htobe32((be32toh(hdr->data) & 0xFFF0FFFF) | ((counter & 0x0F) << 16)); -} - -uint16_t SomeIpSdEntry::getEventgroupId() const -{ - return (uint16_t)(be32toh(getSomeIpSdEntryHeader()->data) & 0x0000FFFF); -} - -void SomeIpSdEntry::setEventgroupId(uint16_t eventgroupID) -{ - someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); - hdr->data = htobe32((be32toh(hdr->data) & 0xFFFF0000) | eventgroupID); -} - -void SomeIpSdEntry::initStdFields(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, - uint32_t TTL) -{ - m_EntryType = type; - m_Layer = nullptr; - m_Offset = 0; - - size_t dataLen = sizeof(someipsdhdrentry); - m_ShadowData = new uint8_t[dataLen]; - memset(m_ShadowData, 0, dataLen); - - someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); - setServiceId(serviceID); - setInstanceId(instanceID); - setMajorVersion(majorVersion); - setTtl(TTL); - - switch (type) - { - case EntryType::FindService: - { - hdr->type = static_cast(TypeInternal::FindService_Internal); - break; - } - case EntryType::OfferService: - case EntryType::StopOfferService: - { - hdr->type = static_cast(TypeInternal::OfferService_Internal); - break; - } - case EntryType::SubscribeEventgroup: - case EntryType::StopSubscribeEventgroup: - { - hdr->type = static_cast(TypeInternal::SubscribeEventgroup_Internal); - break; - } - case EntryType::SubscribeEventgroupAck: - case EntryType::SubscribeEventgroupNack: - { - hdr->type = static_cast(TypeInternal::SubscribeEventgroupAck_Internal); - break; - } - default: - break; - } -} - -/* - * SomeIpSdLayer - */ -SomeIpSdLayer::SomeIpSdLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) - : SomeIpLayer(data, dataLen, prevLayer, packet) -{ - m_NumOptions = countOptions(); -} - -SomeIpSdLayer::SomeIpSdLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, - uint8_t interfaceVersion, MsgType type, uint8_t returnCode, uint8_t flags) -{ - m_Protocol = SomeIP; - m_DataLen = sizeof(someipsdhdr) + 2 * sizeof(uint32_t); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - - m_NumOptions = 0; - - setServiceID(serviceID); - setMethodID(methodID); - setPayloadLength(sizeof(uint32_t) * 3); // Flags+Reserved, Length Entries, Length Options - setClientID(clientID); - setSessionID(sessionID); - setProtocolVersion(0x01); - setInterfaceVersion(interfaceVersion); - setMessageType(type); - setReturnCode(returnCode); - setFlags(flags); -} - -uint8_t SomeIpSdLayer::getFlags() const -{ - someipsdhdr *hdr = (someipsdhdr *)m_Data; - return hdr->flags; -} - -void SomeIpSdLayer::setFlags(uint8_t flags) -{ - someipsdhdr *hdr = (someipsdhdr *)m_Data; - hdr->flags = flags; -} - -uint32_t SomeIpSdLayer::getNumEntries() const -{ - return (uint32_t)(getLenEntries() / sizeof(SomeIpSdEntry::someipsdhdrentry)); -} - -uint32_t SomeIpSdLayer::getNumOptions() const -{ - return m_NumOptions; -} - -const SomeIpSdLayer::EntriesVec SomeIpSdLayer::getEntries() const -{ - size_t remainingLen = getLenEntries(); - size_t offset = sizeof(someipsdhdr) + sizeof(uint32_t); - - EntriesVec vecEntries; - EntryPtr entry; - - while (remainingLen > 0) - { - entry = new SomeIpSdEntry(this, offset); - - size_t entryLen = entry->getLength(); - remainingLen -= entryLen; - offset += entryLen; - - vecEntries.push_back(entry); - } - - return vecEntries; -}; - -const SomeIpSdLayer::OptionsVec SomeIpSdLayer::getOptions() const -{ - OptionsVec vecOptions; - OptionPtr option; - - size_t remainingLen = getLenOptions(); - size_t offset = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries() + sizeof(uint32_t); - - while (remainingLen > 0) - { - SomeIpSdOption::someipsdhdroptionsbase *hdr = (SomeIpSdOption::someipsdhdroptionsbase *)(m_Data + offset); - SomeIpSdOption::OptionType optionType = static_cast(hdr->type); - - option = parseOption(optionType, offset); - - if (option != nullptr) - { - vecOptions.push_back(std::move(option)); - } - - size_t optionLen = be16toh(hdr->length) + 3; - remainingLen -= optionLen; - offset += optionLen; - } - - return vecOptions; -} - -const SomeIpSdLayer::OptionsVec SomeIpSdLayer::getOptionsFromEntry(uint32_t index) const -{ - OptionsVec vecOptions; - OptionPtr option; - - if (index >= getNumEntries()) - return vecOptions; - - size_t remainingLen = getLenOptions(); - size_t offset = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries() + sizeof(uint32_t); - - size_t offsetToEntry = sizeof(someipsdhdr) + sizeof(uint32_t) + index * sizeof(SomeIpSdEntry::someipsdhdrentry); - SomeIpSdEntry::someipsdhdrentry *hdrEntry = (SomeIpSdEntry::someipsdhdrentry *)(m_Data + offsetToEntry); - uint8_t startIdxRun1 = hdrEntry->indexFirstOption; - uint8_t lenRun1 = hdrEntry->nrOpt1; - uint8_t startIdxRun2 = hdrEntry->indexSecondOption; - uint8_t lenRun2 = hdrEntry->nrOpt2; - - int idx = 0; - - while (remainingLen > 0) - { - SomeIpSdOption::someipsdhdroptionsbase *hdrOption = (SomeIpSdOption::someipsdhdroptionsbase *)(m_Data + offset); - - if (((idx >= startIdxRun1) && (idx < (startIdxRun1 + lenRun1))) || - ((idx >= startIdxRun2) && (idx < (startIdxRun2 + lenRun2)))) - { - SomeIpSdOption::OptionType optionType = static_cast(hdrOption->type); - - option = parseOption(optionType, offset); - - if (option != nullptr) - { - vecOptions.push_back(std::move(option)); - } - } - - size_t optionLen = be16toh(hdrOption->length) + 3; - remainingLen -= optionLen; - offset += optionLen; - ++idx; - } - - return vecOptions; -} - -bool SomeIpSdLayer::addOptionTo(uint32_t indexEntry, const SomeIpSdOption &option) -{ - if (indexEntry >= getNumEntries()) - { - return false; - } - - uint32_t indexOption = findOption(option); - bool success = addOptionIndex(indexEntry, indexOption); - - if (!success) - { - return false; - } - - if (indexOption == m_NumOptions) - { - addOption(option); - } - - return true; -} - -std::string SomeIpSdLayer::toString() const -{ - std::stringstream dataStream; - - dataStream << "SOME/IP-SD Layer, " << getNumEntries() << " entries, " << getNumOptions() << " options"; - - return dataStream.str(); -} - -uint32_t SomeIpSdLayer::addEntry(const SomeIpSdEntry &entry) -{ - size_t lenEntries = getLenEntries(); - int offsetToAddAt = sizeof(someipsdhdr) + sizeof(uint32_t) + lenEntries; - - extendLayer(offsetToAddAt, entry.getLength()); - - setLenEntries(lenEntries + entry.getLength()); - - memcpy(m_Data + offsetToAddAt, entry.getDataPtr(), entry.getLength()); - - auto hdr = getSomeIpHeader(); - hdr->length = htobe32(be32toh(hdr->length) + (uint32_t)entry.getLength()); - - return getNumEntries() - 1; -} - -uint32_t SomeIpSdLayer::countOptions() -{ - size_t offsetOption = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries() + sizeof(uint32_t); - size_t lenOptions = getLenOptions(); - uint32_t len = 0; - - uint32_t numOptions = 0; - while (len < lenOptions) - { - uint32_t lenOption = be16toh(*((uint16_t *)(m_Data + offsetOption + len))) + 3 * sizeof(uint8_t); - len += lenOption; - ++numOptions; - } - return numOptions; -} - -uint32_t SomeIpSdLayer::findOption(const SomeIpSdOption &option) -{ - size_t offsetOption = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries() + sizeof(uint32_t); - - uint32_t i = 0; - while (i < m_NumOptions) - { - uint32_t lenOption = be16toh(*((uint16_t *)(m_Data + offsetOption))) + 3 * sizeof(uint8_t); - - if (option.getLength() == lenOption) - { - if (memcmp(m_Data + offsetOption, option.getDataPtr(), option.getLength()) == 0) - { - return i; - } - } - - offsetOption += lenOption; - ++i; - } - return i; -} - -void SomeIpSdLayer::addOption(const SomeIpSdOption &option) -{ - int offsetToAddAt = (int)getHeaderLen(); - - extendLayer(offsetToAddAt, option.getLength()); - memcpy(m_Data + offsetToAddAt, option.getDataPtr(), option.getLength()); - - setLenOptions(uint32_t(getLenOptions() + option.getLength())); - - auto hdr = getSomeIpHeader(); - hdr->length = htobe32(be32toh(hdr->length) + (uint32_t)option.getLength()); - - ++m_NumOptions; -} - -bool SomeIpSdLayer::addOptionIndex(uint32_t indexEntry, uint32_t indexOffset) -{ - /* The SOME/IP-SD protocol supports two option runs. Runs meaning that two different starting indices with differing - length can be provided. Of course, this only works if the indices in both runs are consecutive. - - So, indices like this would work: - 1 2 3 ; 7 8 - - What wouldn't work is this: - 1 2 3 ; 7 9 - 1 3 ; 7 8 - */ - - size_t offsetToAddAt = sizeof(someipsdhdr) + sizeof(uint32_t) + indexEntry*sizeof(SomeIpSdEntry::someipsdhdrentry); - auto hdrEntry = (SomeIpSdEntry::someipsdhdrentry *)(m_Data + offsetToAddAt); - - uint8_t indexFirstOption = hdrEntry->indexFirstOption; - uint8_t lenFirstOption = hdrEntry->nrOpt1; - - if (lenFirstOption == 0) - { - hdrEntry->indexFirstOption = indexOffset; - ++hdrEntry->nrOpt1; - return true; - } - - if (indexFirstOption + lenFirstOption + 1 == indexOffset) - { - ++hdrEntry->nrOpt1; - return true; - } - - uint8_t indexSecondOption = hdrEntry->indexSecondOption; - uint8_t lenSecondOption = hdrEntry->nrOpt2; - - if (lenSecondOption == 0) - { - hdrEntry->indexFirstOption = indexOffset; - ++hdrEntry->nrOpt1; - return true; - } - - if (indexSecondOption + lenSecondOption + 1 == indexOffset) - { - ++hdrEntry->nrOpt2; - return true; - } - - return false; -} - -SomeIpSdLayer::OptionPtr SomeIpSdLayer::parseOption(SomeIpSdOption::OptionType type, size_t offset) const -{ - switch (type) - { - case SomeIpSdOption::OptionType::IPv4Endpoint: - case SomeIpSdOption::OptionType::IPv4Multicast: - case SomeIpSdOption::OptionType::IPv4SdEndpoint: - { - return new SomeIpSdIPv4Option(this, offset); - } - case SomeIpSdOption::OptionType::IPv6Endpoint: - case SomeIpSdOption::OptionType::IPv6Multicast: - case SomeIpSdOption::OptionType::IPv6SdEndpoint: - { - return new SomeIpSdIPv6Option(this, offset); - } - case SomeIpSdOption::OptionType::ConfigurationString: - { - return new SomeIpSdConfigurationOption(this, offset); - } - case SomeIpSdOption::OptionType::LoadBalancing: - { - return new SomeIpSdLoadBalancingOption(this, offset); - } - default: - break; - } - return nullptr; -} - -size_t SomeIpSdLayer::getLenEntries() const -{ - return be32toh(*((uint32_t *)(m_Data + sizeof(someipsdhdr)))); -} - -size_t SomeIpSdLayer::getLenOptions() const -{ - return be32toh(*((uint32_t *)(m_Data + sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries()))); -} - -void SomeIpSdLayer::setLenEntries(uint32_t length) -{ - *((uint32_t *)(m_Data + sizeof(someipsdhdr))) = htobe32(length); -} - -void SomeIpSdLayer::setLenOptions(uint32_t length) -{ - *((uint32_t *)(m_Data + sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries())) = htobe32(length); -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModuleSomeIpSdLayer + +#include "SomeIpSdLayer.h" +#include +#include +#include +#include + +namespace pcpp +{ + +/* + * SomeIpSdOption + */ +SomeIpSdOption::~SomeIpSdOption() +{ + if (m_ShadowData != nullptr) + delete[] m_ShadowData; +} + +SomeIpSdOption::OptionType SomeIpSdOption::getType() const { + return static_cast(getSomeIpSdOptionHeader()->type); +} + +uint8_t* SomeIpSdOption::getDataPtr() const +{ + if (m_DataContainer != nullptr) + return m_DataContainer->getDataPtr(m_Offset); + + return m_ShadowData; +} + +SomeIpSdOption::someipsdhdroptionsbase *SomeIpSdOption::getSomeIpSdOptionHeader() const +{ + return (someipsdhdroptionsbase *)getDataPtr(); +} + +void SomeIpSdOption::initStdFields(OptionType type) +{ + someipsdhdroptionsbase *optionHdr = getSomeIpSdOptionHeader(); + + optionHdr->type = static_cast(type); + /* Length field is excluding length field itself and uint8_t type field */ + optionHdr->length = htobe16((uint16_t)(m_DataLen - sizeof(optionHdr->length) - sizeof(optionHdr->type))); +} + +/* + * SomeIpSdIPv4Option + */ +SomeIpSdIPv4Option::SomeIpSdIPv4Option(IPv4OptionType type, IPv4Address ipAddress, uint16_t port, + SomeIpSdProtocolType l4Protocol) +{ + m_DataLen = sizeof(someipsdhdroptionsipv4); + m_ShadowData = new uint8_t[m_DataLen]; + memset(m_ShadowData, 0, m_DataLen); + + switch(type) + { + case IPv4OptionType::IPv4Endpoint: + initStdFields(OptionType::IPv4Endpoint); + break; + case IPv4OptionType::IPv4Multicast: + initStdFields(OptionType::IPv4Multicast); + break; + case IPv4OptionType::IPv4SdEndpoint: + initStdFields(OptionType::IPv4SdEndpoint); + break; + } + + someipsdhdroptionsipv4 *hdr = (someipsdhdroptionsipv4 *)getDataPtr(); + hdr->ipv4Address = ipAddress.toInt(); + hdr->portNumber = htobe16(port); + hdr->l4Protocol = l4Protocol; +} + +SomeIpSdIPv4Option::SomeIpSdIPv4Option(const IDataContainer *dataContainer, size_t offset) + : SomeIpSdOption(dataContainer, offset) +{ + m_DataLen = sizeof(someipsdhdroptionsipv4); +} + +IPv4Address SomeIpSdIPv4Option::getIpAddress() const +{ + someipsdhdroptionsipv4 *hdr = (someipsdhdroptionsipv4 *)getDataPtr(); + IPv4Address ipAddr(hdr->ipv4Address); + + return ipAddr; +} + +uint16_t SomeIpSdIPv4Option::getPort() const +{ + someipsdhdroptionsipv4 *hdr = (someipsdhdroptionsipv4 *)getDataPtr(); + return be16toh(hdr->portNumber); +} + +SomeIpSdProtocolType SomeIpSdIPv4Option::getProtocol() const +{ + someipsdhdroptionsipv4 *hdr = (someipsdhdroptionsipv4 *)getDataPtr(); + return hdr->l4Protocol; +} + +/* + * SomeIpSdIPv6Option + */ +SomeIpSdIPv6Option::SomeIpSdIPv6Option(IPv6OptionType type, IPv6Address ipAddress, uint16_t port, + SomeIpSdProtocolType l4Protocol) +{ + m_DataLen = sizeof(someipsdhdroptionsipv6); + m_ShadowData = new uint8_t[m_DataLen]; + memset(m_ShadowData, 0, m_DataLen); + + switch(type) + { + case IPv6OptionType::IPv6Endpoint: + initStdFields(OptionType::IPv6Endpoint); + break; + case IPv6OptionType::IPv6Multicast: + initStdFields(OptionType::IPv6Multicast); + break; + case IPv6OptionType::IPv6SdEndpoint: + initStdFields(OptionType::IPv6SdEndpoint); + break; + } + + someipsdhdroptionsipv6 *hdr = (someipsdhdroptionsipv6 *)getDataPtr(); + std::memcpy(hdr->ipv6Address, ipAddress.toBytes(), 16); + hdr->portNumber = htobe16(port); + hdr->l4Protocol = l4Protocol; +} + +SomeIpSdIPv6Option::SomeIpSdIPv6Option(const IDataContainer *dataContainer, size_t offset) + : SomeIpSdOption(dataContainer, offset) +{ + m_DataLen = sizeof(someipsdhdroptionsipv6); +} + +IPv6Address SomeIpSdIPv6Option::getIpAddress() const +{ + someipsdhdroptionsipv6 *hdr = (someipsdhdroptionsipv6 *)getDataPtr(); + IPv6Address ipAddr(hdr->ipv6Address); + + return ipAddr; +} + +uint16_t SomeIpSdIPv6Option::getPort() const +{ + someipsdhdroptionsipv6 *hdr = (someipsdhdroptionsipv6 *)getDataPtr(); + return be16toh(hdr->portNumber); +} + +SomeIpSdProtocolType SomeIpSdIPv6Option::getProtocol() const +{ + someipsdhdroptionsipv6 *hdr = (someipsdhdroptionsipv6 *)getDataPtr(); + return hdr->l4Protocol; +} + +/* + * SomeIpSdConfigurationOption + */ +SomeIpSdConfigurationOption::SomeIpSdConfigurationOption(const std::string &configurationString) +{ + m_DataLen = configurationString.length() + sizeof(someipsdhdroptionsbase); + m_ShadowData = new uint8_t[m_DataLen]; + memset(m_ShadowData, 0, m_DataLen); + + initStdFields(OptionType::ConfigurationString); + std::memcpy(getDataPtr() + sizeof(someipsdhdroptionsbase), configurationString.c_str(), + configurationString.length()); +} + +SomeIpSdConfigurationOption::SomeIpSdConfigurationOption(const IDataContainer *dataContainer, size_t offset) + : SomeIpSdOption(dataContainer, offset) +{ + m_DataLen = sizeof(someipsdhdroptionsbase) - 1 + be16toh(getSomeIpSdOptionHeader()->length); +} + +std::string SomeIpSdConfigurationOption::getConfigurationString() const +{ + return std::string((char *)getDataPtr() + sizeof(someipsdhdroptionsbase), + be16toh(getSomeIpSdOptionHeader()->length) - 1); +} + +/* + * SomeIpSdLoadBalancingOption + */ +SomeIpSdLoadBalancingOption::SomeIpSdLoadBalancingOption(uint16_t priority, uint16_t weight) +{ + m_DataLen = sizeof(someipsdhdroptionsload); + m_ShadowData = new uint8_t[m_DataLen]; + memset(m_ShadowData, 0, m_DataLen); + + initStdFields(OptionType::LoadBalancing); + + someipsdhdroptionsload *hdr = (someipsdhdroptionsload *)getDataPtr(); + hdr->priority = htobe16(priority); + hdr->weight = htobe16(weight); +} + +SomeIpSdLoadBalancingOption::SomeIpSdLoadBalancingOption(const IDataContainer *dataContainer, size_t offset) + : SomeIpSdOption(dataContainer, offset) +{ + m_DataLen = sizeof(someipsdhdroptionsload); +} + +uint16_t SomeIpSdLoadBalancingOption::getPriority() const +{ + someipsdhdroptionsload *hdr = (someipsdhdroptionsload *)getDataPtr(); + return be16toh(hdr->priority); +} + +uint16_t SomeIpSdLoadBalancingOption::getWeight() const +{ + someipsdhdroptionsload *hdr = (someipsdhdroptionsload *)getDataPtr(); + return be16toh(hdr->weight); +} + +/* + * SomeIpSdEntry + */ + +SomeIpSdEntry::SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, + uint32_t TTL, uint32_t minorVersion) +{ + initStdFields(type, serviceID, instanceID, majorVersion, TTL); + setMinorVersion(minorVersion); +} + +SomeIpSdEntry::SomeIpSdEntry(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, + uint32_t TTL, uint8_t counter, uint16_t eventGroupID) +{ + initStdFields(type, serviceID, instanceID, majorVersion, TTL); + setCounter(counter); + setEventgroupId(eventGroupID); +} + +SomeIpSdEntry::SomeIpSdEntry(const SomeIpSdLayer *pSomeIpSdLayer, size_t offset) + : m_Layer(pSomeIpSdLayer), m_Offset(offset), m_ShadowData(nullptr) +{ + EntryType entryType; + + someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); + TypeInternal internalType = static_cast(hdr->type); + auto ttl = getTtl(); + + switch(internalType) + { + case SomeIpSdEntry::TypeInternal::FindService_Internal: + entryType = SomeIpSdEntry::EntryType::FindService; + break; + case SomeIpSdEntry::TypeInternal::OfferService_Internal: + if (ttl == 0) + { + entryType = EntryType::StopOfferService; + } + else + { + entryType = EntryType::OfferService; + } + break; + case SomeIpSdEntry::TypeInternal::SubscribeEventgroup_Internal: + if (ttl == 0) + { + entryType = EntryType::StopSubscribeEventgroup; + } + else + { + entryType = EntryType::SubscribeEventgroup; + } + break; + case SomeIpSdEntry::TypeInternal::SubscribeEventgroupAck_Internal: + if (ttl == 0) + { + entryType = EntryType::SubscribeEventgroupNack; + } + else + { + entryType = EntryType::SubscribeEventgroupAck; + } + break; + default: + entryType = EntryType::UnknownEntryType; + break; + } + + m_EntryType = entryType; +} + +SomeIpSdEntry::~SomeIpSdEntry() +{ + if (m_ShadowData != nullptr) + delete[] m_ShadowData; +} + +uint8_t *SomeIpSdEntry::getDataPtr() const +{ + if (m_Layer != nullptr) + return m_Layer->getDataPtr(m_Offset); + + return m_ShadowData; +} + +SomeIpSdEntry::someipsdhdrentry *SomeIpSdEntry::getSomeIpSdEntryHeader() const +{ + return (someipsdhdrentry *)getDataPtr(); +} + +uint32_t SomeIpSdEntry::getNumOptions() const +{ + auto *hdr = getSomeIpSdEntryHeader(); + return hdr->nrOpt1 + hdr->nrOpt2; +} + +uint16_t SomeIpSdEntry::getServiceId() const +{ + return be16toh(getSomeIpSdEntryHeader()->serviceID); +} + +void SomeIpSdEntry::setServiceId(uint16_t serviceId) +{ + getSomeIpSdEntryHeader()->serviceID = htobe16(serviceId); +} + +uint16_t SomeIpSdEntry::getInstanceId() const +{ + return be16toh(getSomeIpSdEntryHeader()->instanceID); +} + +void SomeIpSdEntry::setInstanceId(uint16_t instanceId) +{ + getSomeIpSdEntryHeader()->instanceID = htobe16(instanceId); +} + +uint8_t SomeIpSdEntry::getMajorVersion() const +{ + return (be32toh(getSomeIpSdEntryHeader()->majorVersion_ttl) & ~SOMEIPSD_HDR_ENTRY_MASK_TTL) >> 24; +} + +void SomeIpSdEntry::setMajorVersion(uint8_t majorVersion) +{ + someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); + uint32_t val = (majorVersion << 24) | (be32toh(hdr->majorVersion_ttl) & SOMEIPSD_HDR_ENTRY_MASK_TTL); + hdr->majorVersion_ttl = htobe32(val); +} + +uint32_t SomeIpSdEntry::getTtl() const +{ + return be32toh(getSomeIpSdEntryHeader()->majorVersion_ttl) & SOMEIPSD_HDR_ENTRY_MASK_TTL; +} + +void SomeIpSdEntry::setTtl(uint32_t ttl) +{ + someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); + uint32_t val = (ttl & SOMEIPSD_HDR_ENTRY_MASK_TTL) | (be32toh(hdr->majorVersion_ttl) & ~SOMEIPSD_HDR_ENTRY_MASK_TTL); + hdr->majorVersion_ttl = htobe32(val); +} + +uint32_t SomeIpSdEntry::getMinorVersion() const +{ + return be32toh(getSomeIpSdEntryHeader()->data); +} + +void SomeIpSdEntry::setMinorVersion(uint32_t minorVersion) +{ + getSomeIpSdEntryHeader()->data = htobe32(minorVersion); +} + +uint8_t SomeIpSdEntry::getCounter() const +{ + return (uint8_t)((be32toh(getSomeIpSdEntryHeader()->data) >> 16) & 0x0F); +} + +void SomeIpSdEntry::setCounter(uint8_t counter) +{ + someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); + hdr->data = htobe32((be32toh(hdr->data) & 0xFFF0FFFF) | ((counter & 0x0F) << 16)); +} + +uint16_t SomeIpSdEntry::getEventgroupId() const +{ + return (uint16_t)(be32toh(getSomeIpSdEntryHeader()->data) & 0x0000FFFF); +} + +void SomeIpSdEntry::setEventgroupId(uint16_t eventgroupID) +{ + someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); + hdr->data = htobe32((be32toh(hdr->data) & 0xFFFF0000) | eventgroupID); +} + +void SomeIpSdEntry::initStdFields(EntryType type, uint16_t serviceID, uint16_t instanceID, uint8_t majorVersion, + uint32_t TTL) +{ + m_EntryType = type; + m_Layer = nullptr; + m_Offset = 0; + + size_t dataLen = sizeof(someipsdhdrentry); + m_ShadowData = new uint8_t[dataLen]; + memset(m_ShadowData, 0, dataLen); + + someipsdhdrentry *hdr = getSomeIpSdEntryHeader(); + setServiceId(serviceID); + setInstanceId(instanceID); + setMajorVersion(majorVersion); + setTtl(TTL); + + switch (type) + { + case EntryType::FindService: + { + hdr->type = static_cast(TypeInternal::FindService_Internal); + break; + } + case EntryType::OfferService: + case EntryType::StopOfferService: + { + hdr->type = static_cast(TypeInternal::OfferService_Internal); + break; + } + case EntryType::SubscribeEventgroup: + case EntryType::StopSubscribeEventgroup: + { + hdr->type = static_cast(TypeInternal::SubscribeEventgroup_Internal); + break; + } + case EntryType::SubscribeEventgroupAck: + case EntryType::SubscribeEventgroupNack: + { + hdr->type = static_cast(TypeInternal::SubscribeEventgroupAck_Internal); + break; + } + default: + break; + } +} + +/* + * SomeIpSdLayer + */ +SomeIpSdLayer::SomeIpSdLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) + : SomeIpLayer(data, dataLen, prevLayer, packet) +{ + m_NumOptions = countOptions(); +} + +SomeIpSdLayer::SomeIpSdLayer(uint16_t serviceID, uint16_t methodID, uint16_t clientID, uint16_t sessionID, + uint8_t interfaceVersion, MsgType type, uint8_t returnCode, uint8_t flags) +{ + m_Protocol = SomeIP; + m_DataLen = sizeof(someipsdhdr) + 2 * sizeof(uint32_t); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + + m_NumOptions = 0; + + setServiceID(serviceID); + setMethodID(methodID); + setPayloadLength(sizeof(uint32_t) * 3); // Flags+Reserved, Length Entries, Length Options + setClientID(clientID); + setSessionID(sessionID); + setProtocolVersion(0x01); + setInterfaceVersion(interfaceVersion); + setMessageType(type); + setReturnCode(returnCode); + setFlags(flags); +} + +uint8_t SomeIpSdLayer::getFlags() const +{ + someipsdhdr *hdr = (someipsdhdr *)m_Data; + return hdr->flags; +} + +void SomeIpSdLayer::setFlags(uint8_t flags) +{ + someipsdhdr *hdr = (someipsdhdr *)m_Data; + hdr->flags = flags; +} + +uint32_t SomeIpSdLayer::getNumEntries() const +{ + return (uint32_t)(getLenEntries() / sizeof(SomeIpSdEntry::someipsdhdrentry)); +} + +uint32_t SomeIpSdLayer::getNumOptions() const +{ + return m_NumOptions; +} + +const SomeIpSdLayer::EntriesVec SomeIpSdLayer::getEntries() const +{ + size_t remainingLen = getLenEntries(); + size_t offset = sizeof(someipsdhdr) + sizeof(uint32_t); + + EntriesVec vecEntries; + EntryPtr entry; + + while (remainingLen > 0) + { + entry = new SomeIpSdEntry(this, offset); + + size_t entryLen = entry->getLength(); + remainingLen -= entryLen; + offset += entryLen; + + vecEntries.push_back(entry); + } + + return vecEntries; +}; + +const SomeIpSdLayer::OptionsVec SomeIpSdLayer::getOptions() const +{ + OptionsVec vecOptions; + OptionPtr option; + + size_t remainingLen = getLenOptions(); + size_t offset = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries() + sizeof(uint32_t); + + while (remainingLen > 0) + { + SomeIpSdOption::someipsdhdroptionsbase *hdr = (SomeIpSdOption::someipsdhdroptionsbase *)(m_Data + offset); + SomeIpSdOption::OptionType optionType = static_cast(hdr->type); + + option = parseOption(optionType, offset); + + if (option != nullptr) + { + vecOptions.push_back(std::move(option)); + } + + size_t optionLen = be16toh(hdr->length) + 3; + remainingLen -= optionLen; + offset += optionLen; + } + + return vecOptions; +} + +const SomeIpSdLayer::OptionsVec SomeIpSdLayer::getOptionsFromEntry(uint32_t index) const +{ + OptionsVec vecOptions; + OptionPtr option; + + if (index >= getNumEntries()) + return vecOptions; + + size_t remainingLen = getLenOptions(); + size_t offset = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries() + sizeof(uint32_t); + + size_t offsetToEntry = sizeof(someipsdhdr) + sizeof(uint32_t) + index * sizeof(SomeIpSdEntry::someipsdhdrentry); + SomeIpSdEntry::someipsdhdrentry *hdrEntry = (SomeIpSdEntry::someipsdhdrentry *)(m_Data + offsetToEntry); + uint8_t startIdxRun1 = hdrEntry->indexFirstOption; + uint8_t lenRun1 = hdrEntry->nrOpt1; + uint8_t startIdxRun2 = hdrEntry->indexSecondOption; + uint8_t lenRun2 = hdrEntry->nrOpt2; + + int idx = 0; + + while (remainingLen > 0) + { + SomeIpSdOption::someipsdhdroptionsbase *hdrOption = (SomeIpSdOption::someipsdhdroptionsbase *)(m_Data + offset); + + if (((idx >= startIdxRun1) && (idx < (startIdxRun1 + lenRun1))) || + ((idx >= startIdxRun2) && (idx < (startIdxRun2 + lenRun2)))) + { + SomeIpSdOption::OptionType optionType = static_cast(hdrOption->type); + + option = parseOption(optionType, offset); + + if (option != nullptr) + { + vecOptions.push_back(std::move(option)); + } + } + + size_t optionLen = be16toh(hdrOption->length) + 3; + remainingLen -= optionLen; + offset += optionLen; + ++idx; + } + + return vecOptions; +} + +bool SomeIpSdLayer::addOptionTo(uint32_t indexEntry, const SomeIpSdOption &option) +{ + if (indexEntry >= getNumEntries()) + { + return false; + } + + uint32_t indexOption = findOption(option); + bool success = addOptionIndex(indexEntry, indexOption); + + if (!success) + { + return false; + } + + if (indexOption == m_NumOptions) + { + addOption(option); + } + + return true; +} + +std::string SomeIpSdLayer::toString() const +{ + std::stringstream dataStream; + + dataStream << "SOME/IP-SD Layer, " << getNumEntries() << " entries, " << getNumOptions() << " options"; + + return dataStream.str(); +} + +uint32_t SomeIpSdLayer::addEntry(const SomeIpSdEntry &entry) +{ + size_t lenEntries = getLenEntries(); + int offsetToAddAt = sizeof(someipsdhdr) + sizeof(uint32_t) + lenEntries; + + extendLayer(offsetToAddAt, entry.getLength()); + + setLenEntries(lenEntries + entry.getLength()); + + memcpy(m_Data + offsetToAddAt, entry.getDataPtr(), entry.getLength()); + + auto hdr = getSomeIpHeader(); + hdr->length = htobe32(be32toh(hdr->length) + (uint32_t)entry.getLength()); + + return getNumEntries() - 1; +} + +uint32_t SomeIpSdLayer::countOptions() +{ + size_t offsetOption = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries() + sizeof(uint32_t); + size_t lenOptions = getLenOptions(); + uint32_t len = 0; + + uint32_t numOptions = 0; + while (len < lenOptions) + { + uint32_t lenOption = be16toh(*((uint16_t *)(m_Data + offsetOption + len))) + 3 * sizeof(uint8_t); + len += lenOption; + ++numOptions; + } + return numOptions; +} + +uint32_t SomeIpSdLayer::findOption(const SomeIpSdOption &option) +{ + size_t offsetOption = sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries() + sizeof(uint32_t); + + uint32_t i = 0; + while (i < m_NumOptions) + { + uint32_t lenOption = be16toh(*((uint16_t *)(m_Data + offsetOption))) + 3 * sizeof(uint8_t); + + if (option.getLength() == lenOption) + { + if (memcmp(m_Data + offsetOption, option.getDataPtr(), option.getLength()) == 0) + { + return i; + } + } + + offsetOption += lenOption; + ++i; + } + return i; +} + +void SomeIpSdLayer::addOption(const SomeIpSdOption &option) +{ + int offsetToAddAt = (int)getHeaderLen(); + + extendLayer(offsetToAddAt, option.getLength()); + memcpy(m_Data + offsetToAddAt, option.getDataPtr(), option.getLength()); + + setLenOptions(uint32_t(getLenOptions() + option.getLength())); + + auto hdr = getSomeIpHeader(); + hdr->length = htobe32(be32toh(hdr->length) + (uint32_t)option.getLength()); + + ++m_NumOptions; +} + +bool SomeIpSdLayer::addOptionIndex(uint32_t indexEntry, uint32_t indexOffset) +{ + /* The SOME/IP-SD protocol supports two option runs. Runs meaning that two different starting indices with differing + length can be provided. Of course, this only works if the indices in both runs are consecutive. + + So, indices like this would work: + 1 2 3 ; 7 8 + + What wouldn't work is this: + 1 2 3 ; 7 9 + 1 3 ; 7 8 + */ + + size_t offsetToAddAt = sizeof(someipsdhdr) + sizeof(uint32_t) + indexEntry*sizeof(SomeIpSdEntry::someipsdhdrentry); + auto hdrEntry = (SomeIpSdEntry::someipsdhdrentry *)(m_Data + offsetToAddAt); + + uint8_t indexFirstOption = hdrEntry->indexFirstOption; + uint8_t lenFirstOption = hdrEntry->nrOpt1; + + if (lenFirstOption == 0) + { + hdrEntry->indexFirstOption = indexOffset; + ++hdrEntry->nrOpt1; + return true; + } + + if (indexFirstOption + lenFirstOption + 1 == indexOffset) + { + ++hdrEntry->nrOpt1; + return true; + } + + uint8_t indexSecondOption = hdrEntry->indexSecondOption; + uint8_t lenSecondOption = hdrEntry->nrOpt2; + + if (lenSecondOption == 0) + { + hdrEntry->indexFirstOption = indexOffset; + ++hdrEntry->nrOpt1; + return true; + } + + if (indexSecondOption + lenSecondOption + 1 == indexOffset) + { + ++hdrEntry->nrOpt2; + return true; + } + + return false; +} + +SomeIpSdLayer::OptionPtr SomeIpSdLayer::parseOption(SomeIpSdOption::OptionType type, size_t offset) const +{ + switch (type) + { + case SomeIpSdOption::OptionType::IPv4Endpoint: + case SomeIpSdOption::OptionType::IPv4Multicast: + case SomeIpSdOption::OptionType::IPv4SdEndpoint: + { + return new SomeIpSdIPv4Option(this, offset); + } + case SomeIpSdOption::OptionType::IPv6Endpoint: + case SomeIpSdOption::OptionType::IPv6Multicast: + case SomeIpSdOption::OptionType::IPv6SdEndpoint: + { + return new SomeIpSdIPv6Option(this, offset); + } + case SomeIpSdOption::OptionType::ConfigurationString: + { + return new SomeIpSdConfigurationOption(this, offset); + } + case SomeIpSdOption::OptionType::LoadBalancing: + { + return new SomeIpSdLoadBalancingOption(this, offset); + } + default: + break; + } + return nullptr; +} + +size_t SomeIpSdLayer::getLenEntries() const +{ + return be32toh(*((uint32_t *)(m_Data + sizeof(someipsdhdr)))); +} + +size_t SomeIpSdLayer::getLenOptions() const +{ + return be32toh(*((uint32_t *)(m_Data + sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries()))); +} + +void SomeIpSdLayer::setLenEntries(uint32_t length) +{ + *((uint32_t *)(m_Data + sizeof(someipsdhdr))) = htobe32(length); +} + +void SomeIpSdLayer::setLenOptions(uint32_t length) +{ + *((uint32_t *)(m_Data + sizeof(someipsdhdr) + sizeof(uint32_t) + getLenEntries())) = htobe32(length); +} + +} // namespace pcpp diff --git a/Packet++/src/TcpLayer.cpp b/Packet++/src/TcpLayer.cpp index e0e489e780..082af10cc3 100644 --- a/Packet++/src/TcpLayer.cpp +++ b/Packet++/src/TcpLayer.cpp @@ -1,433 +1,433 @@ -#define LOG_MODULE PacketLogModuleTcpLayer - -#include "EndianPortable.h" -#include "TcpLayer.h" -#include "IPv4Layer.h" -#include "IPv6Layer.h" -#include "PayloadLayer.h" -#include "HttpLayer.h" -#include "SSLLayer.h" -#include "SipLayer.h" -#include "BgpLayer.h" -#include "SSHLayer.h" -#include "DnsLayer.h" -#include "TelnetLayer.h" -#include "FtpLayer.h" -#include "SomeIpLayer.h" -#include "PacketUtils.h" -#include "Logger.h" -#include -#include - -namespace pcpp -{ - -#define TCPOPT_DUMMY 0xff - -/// ~~~~~~~~~~~~~~~~ -/// TcpOptionBuilder -/// ~~~~~~~~~~~~~~~~ - -TcpOptionBuilder::TcpOptionBuilder(NopEolOptionTypes optionType) -{ - switch (optionType) - { - case EOL: - init((uint8_t)PCPP_TCPOPT_EOL, nullptr, 0); - break; - case NOP: - default: - init((uint8_t)PCPP_TCPOPT_NOP, nullptr, 0); - break; - } -} - -TcpOption TcpOptionBuilder::build() const -{ - uint8_t recType = static_cast(m_RecType); - size_t optionSize = m_RecValueLen + 2*sizeof(uint8_t); - - if (recType == (uint8_t)PCPP_TCPOPT_EOL || recType == (uint8_t)PCPP_TCPOPT_NOP) - { - if (m_RecValueLen != 0) - { - PCPP_LOG_ERROR("TCP NOP and TCP EOL options are 1-byte long and don't have option value. Tried to set option value of size " << m_RecValueLen); - return TcpOption(nullptr); - } - - optionSize = 1; - } - - uint8_t* recordBuffer = new uint8_t[optionSize]; - memset(recordBuffer, 0, optionSize); - recordBuffer[0] = recType; - if (optionSize > 1) - { - recordBuffer[1] = static_cast(optionSize); - if (optionSize > 2 && m_RecValue != nullptr) - memcpy(recordBuffer+2, m_RecValue, m_RecValueLen); - } - - return TcpOption(recordBuffer); -} - - - -/// ~~~~~~~~ -/// TcpLayer -/// ~~~~~~~~ - -uint16_t TcpLayer::getSrcPort() const -{ - return be16toh(getTcpHeader()->portSrc); -} - -uint16_t TcpLayer::getDstPort() const -{ - return be16toh(getTcpHeader()->portDst); -} - -TcpOption TcpLayer::getTcpOption(TcpOptionType option) const -{ - return m_OptionReader.getTLVRecord((uint8_t)option, getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); -} - -TcpOption TcpLayer::getFirstTcpOption() const -{ - return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); -} - -TcpOption TcpLayer::getNextTcpOption(TcpOption& tcpOption) const -{ - TcpOption nextOpt = m_OptionReader.getNextTLVRecord(tcpOption, getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); - if (nextOpt.isNotNull() && nextOpt.getType() == TCPOPT_DUMMY) - return TcpOption(nullptr); - - return nextOpt; -} - -size_t TcpLayer::getTcpOptionCount() const -{ - return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); -} - -TcpOption TcpLayer::addTcpOption(const TcpOptionBuilder& optionBuilder) -{ - return addTcpOptionAt(optionBuilder, getHeaderLen()-m_NumOfTrailingBytes); -} - -TcpOption TcpLayer::addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionType prevOptionType) -{ - int offset = 0; - - if (prevOptionType == TCPOPT_Unknown) - { - offset = sizeof(tcphdr); - } - else - { - TcpOption prevOpt = getTcpOption(prevOptionType); - if (prevOpt.isNull()) - { - PCPP_LOG_ERROR("Previous option of type " << (int)prevOptionType << " not found, cannot add a new TCP option"); - return TcpOption(nullptr); - } - - offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; - } - - return addTcpOptionAt(optionBuilder, offset); -} - -bool TcpLayer::removeTcpOption(TcpOptionType optionType) -{ - TcpOption opt = getTcpOption(optionType); - if (opt.isNull()) - { - return false; - } - - // calculate total TCP option size - TcpOption curOpt = getFirstTcpOption(); - size_t totalOptSize = 0; - while (!curOpt.isNull()) - { - totalOptSize += curOpt.getTotalSize(); - curOpt = getNextTcpOption(curOpt); - } - totalOptSize -= opt.getTotalSize(); - - - int offset = opt.getRecordBasePtr() - m_Data; - - if (!shortenLayer(offset, opt.getTotalSize())) - { - return false; - } - - adjustTcpOptionTrailer(totalOptSize); - - m_OptionReader.changeTLVRecordCount(-1); - - return true; -} - -bool TcpLayer::removeAllTcpOptions() -{ - int offset = sizeof(tcphdr); - - if (!shortenLayer(offset, getHeaderLen()-offset)) - return false; - - getTcpHeader()->dataOffset = sizeof(tcphdr)/4; - m_NumOfTrailingBytes = 0; - m_OptionReader.changeTLVRecordCount(0-getTcpOptionCount()); - return true; -} - -TcpOption TcpLayer::addTcpOptionAt(const TcpOptionBuilder& optionBuilder, int offset) -{ - TcpOption newOption = optionBuilder.build(); - if (newOption.isNull()) - return newOption; - - // calculate total TCP option size - TcpOption curOpt = getFirstTcpOption(); - size_t totalOptSize = 0; - while (!curOpt.isNull()) - { - totalOptSize += curOpt.getTotalSize(); - curOpt = getNextTcpOption(curOpt); - } - totalOptSize += newOption.getTotalSize(); - - size_t sizeToExtend = newOption.getTotalSize(); - - if (!extendLayer(offset, sizeToExtend)) - { - PCPP_LOG_ERROR("Could not extend TcpLayer in [" << sizeToExtend << "] bytes"); - newOption.purgeRecordData(); - return TcpOption(nullptr); - } - - memcpy(m_Data + offset, newOption.getRecordBasePtr(), newOption.getTotalSize()); - - newOption.purgeRecordData(); - - adjustTcpOptionTrailer(totalOptSize); - - m_OptionReader.changeTLVRecordCount(1); - - uint8_t* newOptPtr = m_Data + offset; - - return TcpOption(newOptPtr); -} - -void TcpLayer::adjustTcpOptionTrailer(size_t totalOptSize) -{ - int newNumberOfTrailingBytes = 0; - while ((totalOptSize + newNumberOfTrailingBytes) % 4 != 0) - newNumberOfTrailingBytes++; - - if (newNumberOfTrailingBytes < m_NumOfTrailingBytes) - shortenLayer(sizeof(tcphdr)+totalOptSize, m_NumOfTrailingBytes - newNumberOfTrailingBytes - 1); - else if (newNumberOfTrailingBytes > m_NumOfTrailingBytes) - extendLayer(sizeof(tcphdr)+totalOptSize, newNumberOfTrailingBytes - m_NumOfTrailingBytes); - - m_NumOfTrailingBytes = newNumberOfTrailingBytes; - - for (int i = 0; i < m_NumOfTrailingBytes; i++) - m_Data[sizeof(tcphdr) + totalOptSize + i] = TCPOPT_DUMMY; - - getTcpHeader()->dataOffset = (sizeof(tcphdr) + totalOptSize + m_NumOfTrailingBytes)/4; -} - -uint16_t TcpLayer::calculateChecksum(bool writeResultToPacket) -{ - tcphdr* tcpHdr = getTcpHeader(); - uint16_t checksumRes = 0; - uint16_t currChecksumValue = tcpHdr->headerChecksum; - - if (m_PrevLayer != nullptr) - { - tcpHdr->headerChecksum = 0; - ScalarBuffer vec[2]; - PCPP_LOG_DEBUG("data len = " << m_DataLen); - vec[0].buffer = (uint16_t*)m_Data; - vec[0].len = m_DataLen; - - if (m_PrevLayer->getProtocol() == IPv4) - { - uint32_t srcIP = ((IPv4Layer*)m_PrevLayer)->getSrcIPv4Address().toInt(); - uint32_t dstIP = ((IPv4Layer*)m_PrevLayer)->getDstIPv4Address().toInt(); - uint16_t pseudoHeader[6]; - pseudoHeader[0] = srcIP >> 16; - pseudoHeader[1] = srcIP & 0xFFFF; - pseudoHeader[2] = dstIP >> 16; - pseudoHeader[3] = dstIP & 0xFFFF; - pseudoHeader[4] = 0xffff & htobe16(m_DataLen); - pseudoHeader[5] = htobe16(0x00ff & PACKETPP_IPPROTO_TCP); - vec[1].buffer = pseudoHeader; - vec[1].len = 12; - checksumRes = computeChecksum(vec, 2); - PCPP_LOG_DEBUG("calculated checksum = 0x" << std::uppercase << std::hex << checksumRes); - - - } - else if (m_PrevLayer->getProtocol() == IPv6) - { - uint16_t pseudoHeader[18]; - ((IPv6Layer*)m_PrevLayer)->getSrcIPv6Address().copyTo((uint8_t*)pseudoHeader); - ((IPv6Layer*)m_PrevLayer)->getDstIPv6Address().copyTo((uint8_t*)(pseudoHeader+8)); - pseudoHeader[16] = 0xffff & htobe16(m_DataLen); - pseudoHeader[17] = htobe16(0x00ff & PACKETPP_IPPROTO_TCP); - vec[1].buffer = pseudoHeader; - vec[1].len = 36; - checksumRes = computeChecksum(vec, 2); - PCPP_LOG_DEBUG("calculated checksum = 0xX" << std::uppercase << std::hex << checksumRes); - } - } - - if(writeResultToPacket) - tcpHdr->headerChecksum = htobe16(checksumRes); - else - tcpHdr->headerChecksum = currChecksumValue; - - return checksumRes; -} - -void TcpLayer::initLayer() -{ - m_DataLen = sizeof(tcphdr); - m_Data = new uint8_t[m_DataLen]; - memset(m_Data, 0, m_DataLen); - m_Protocol = TCP; - m_NumOfTrailingBytes = 0; - getTcpHeader()->dataOffset = sizeof(tcphdr)/4; -} - -TcpLayer::TcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) -{ - m_Protocol = TCP; - m_NumOfTrailingBytes = 0; -} - -TcpLayer::TcpLayer() -{ - initLayer(); -} - -TcpLayer::TcpLayer(uint16_t portSrc, uint16_t portDst) -{ - initLayer(); - getTcpHeader()->portDst = htobe16(portDst); - getTcpHeader()->portSrc = htobe16(portSrc); -} - -void TcpLayer::copyLayerData(const TcpLayer& other) -{ - m_OptionReader = other.m_OptionReader; - m_NumOfTrailingBytes = other.m_NumOfTrailingBytes; -} - -TcpLayer::TcpLayer(const TcpLayer& other) : Layer(other) -{ - copyLayerData(other); -} - -TcpLayer& TcpLayer::operator=(const TcpLayer& other) -{ - Layer::operator=(other); - - copyLayerData(other); - - return *this; -} - -void TcpLayer::parseNextLayer() -{ - size_t headerLen = getHeaderLen(); - if (m_DataLen <= headerLen) - return; - - uint8_t* payload = m_Data + headerLen; - size_t payloadLen = m_DataLen - headerLen; - uint16_t portDst = getDstPort(); - uint16_t portSrc = getSrcPort(); - - if (HttpMessage::isHttpPort(portDst) && HttpRequestFirstLine::parseMethod((char*)payload, payloadLen) != HttpRequestLayer::HttpMethodUnknown) - m_NextLayer = new HttpRequestLayer(payload, payloadLen, this, m_Packet); - else if (HttpMessage::isHttpPort(portSrc) && HttpResponseFirstLine::parseStatusCode((char*)payload, payloadLen) != HttpResponseLayer::HttpStatusCodeUnknown) - m_NextLayer = new HttpResponseLayer(payload, payloadLen, this, m_Packet); - else if (SSLLayer::IsSSLMessage(portSrc, portDst, payload, payloadLen)) - m_NextLayer = SSLLayer::createSSLMessage(payload, payloadLen, this, m_Packet); - else if (SipLayer::isSipPort(portDst) || SipLayer::isSipPort(portSrc)) - { - if (SipRequestFirstLine::parseMethod((char*)payload, payloadLen) != SipRequestLayer::SipMethodUnknown) - m_NextLayer = new SipRequestLayer(payload, payloadLen, this, m_Packet); - else if (SipResponseFirstLine::parseStatusCode((char*)payload, payloadLen) != SipResponseLayer::SipStatusCodeUnknown) - m_NextLayer = new SipResponseLayer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } - else if (BgpLayer::isBgpPort(portSrc, portDst)) - { - m_NextLayer = BgpLayer::parseBgpLayer(payload, payloadLen, this, m_Packet); - if (!m_NextLayer) - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); - } - else if (SSHLayer::isSSHPort(portSrc, portDst)) - m_NextLayer = SSHLayer::createSSHMessage(payload, payloadLen, this, m_Packet); - else if (DnsLayer::isDataValid(payload, payloadLen, true) && (DnsLayer::isDnsPort(portDst) || DnsLayer::isDnsPort(portSrc))) - m_NextLayer = new DnsOverTcpLayer(payload, payloadLen, this, m_Packet); - else if (TelnetLayer::isDataValid(payload, payloadLen) && (TelnetLayer::isTelnetPort(portDst) || TelnetLayer::isTelnetPort(portSrc))) - m_NextLayer = new TelnetLayer(payload, payloadLen, this, m_Packet); - else if (FtpLayer::isFtpPort(portSrc) && FtpLayer::isDataValid(payload, payloadLen)) - m_NextLayer = new FtpResponseLayer(payload, payloadLen, this, m_Packet); - else if (FtpLayer::isFtpPort(portDst) && FtpLayer::isDataValid(payload, payloadLen)) - m_NextLayer = new FtpRequestLayer(payload, payloadLen, this, m_Packet); - else if (SomeIpLayer::isSomeIpPort(portSrc) || SomeIpLayer::isSomeIpPort(portDst)) - m_NextLayer = SomeIpLayer::parseSomeIpLayer(payload, payloadLen, this, m_Packet); - else - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); -} - -void TcpLayer::computeCalculateFields() -{ - tcphdr* tcpHdr = getTcpHeader(); - - tcpHdr->dataOffset = getHeaderLen() >> 2; - calculateChecksum(true); -} - -std::string TcpLayer::toString() const -{ - tcphdr* hdr = getTcpHeader(); - std::string result = "TCP Layer, "; - if (hdr->synFlag) - { - if (hdr->ackFlag) - result += "[SYN, ACK], "; - else - result += "[SYN], "; - } - else if (hdr->finFlag) - { - if (hdr->ackFlag) - result += "[FIN, ACK], "; - else - result += "[FIN], "; - } - else if (hdr->ackFlag) - result += "[ACK], "; - - std::ostringstream srcPortStream; - srcPortStream << getSrcPort(); - std::ostringstream dstPortStream; - dstPortStream << getDstPort(); - result += "Src port: " + srcPortStream.str() + ", Dst port: " + dstPortStream.str(); - - return result; -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModuleTcpLayer + +#include "EndianPortable.h" +#include "TcpLayer.h" +#include "IPv4Layer.h" +#include "IPv6Layer.h" +#include "PayloadLayer.h" +#include "HttpLayer.h" +#include "SSLLayer.h" +#include "SipLayer.h" +#include "BgpLayer.h" +#include "SSHLayer.h" +#include "DnsLayer.h" +#include "TelnetLayer.h" +#include "FtpLayer.h" +#include "SomeIpLayer.h" +#include "PacketUtils.h" +#include "Logger.h" +#include +#include + +namespace pcpp +{ + +#define TCPOPT_DUMMY 0xff + +/// ~~~~~~~~~~~~~~~~ +/// TcpOptionBuilder +/// ~~~~~~~~~~~~~~~~ + +TcpOptionBuilder::TcpOptionBuilder(NopEolOptionTypes optionType) +{ + switch (optionType) + { + case EOL: + init((uint8_t)PCPP_TCPOPT_EOL, nullptr, 0); + break; + case NOP: + default: + init((uint8_t)PCPP_TCPOPT_NOP, nullptr, 0); + break; + } +} + +TcpOption TcpOptionBuilder::build() const +{ + uint8_t recType = static_cast(m_RecType); + size_t optionSize = m_RecValueLen + 2*sizeof(uint8_t); + + if (recType == (uint8_t)PCPP_TCPOPT_EOL || recType == (uint8_t)PCPP_TCPOPT_NOP) + { + if (m_RecValueLen != 0) + { + PCPP_LOG_ERROR("TCP NOP and TCP EOL options are 1-byte long and don't have option value. Tried to set option value of size " << m_RecValueLen); + return TcpOption(nullptr); + } + + optionSize = 1; + } + + uint8_t* recordBuffer = new uint8_t[optionSize]; + memset(recordBuffer, 0, optionSize); + recordBuffer[0] = recType; + if (optionSize > 1) + { + recordBuffer[1] = static_cast(optionSize); + if (optionSize > 2 && m_RecValue != nullptr) + memcpy(recordBuffer+2, m_RecValue, m_RecValueLen); + } + + return TcpOption(recordBuffer); +} + + + +/// ~~~~~~~~ +/// TcpLayer +/// ~~~~~~~~ + +uint16_t TcpLayer::getSrcPort() const +{ + return be16toh(getTcpHeader()->portSrc); +} + +uint16_t TcpLayer::getDstPort() const +{ + return be16toh(getTcpHeader()->portDst); +} + +TcpOption TcpLayer::getTcpOption(TcpOptionType option) const +{ + return m_OptionReader.getTLVRecord((uint8_t)option, getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); +} + +TcpOption TcpLayer::getFirstTcpOption() const +{ + return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); +} + +TcpOption TcpLayer::getNextTcpOption(TcpOption& tcpOption) const +{ + TcpOption nextOpt = m_OptionReader.getNextTLVRecord(tcpOption, getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); + if (nextOpt.isNotNull() && nextOpt.getType() == TCPOPT_DUMMY) + return TcpOption(nullptr); + + return nextOpt; +} + +size_t TcpLayer::getTcpOptionCount() const +{ + return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr)); +} + +TcpOption TcpLayer::addTcpOption(const TcpOptionBuilder& optionBuilder) +{ + return addTcpOptionAt(optionBuilder, getHeaderLen()-m_NumOfTrailingBytes); +} + +TcpOption TcpLayer::addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionType prevOptionType) +{ + int offset = 0; + + if (prevOptionType == TCPOPT_Unknown) + { + offset = sizeof(tcphdr); + } + else + { + TcpOption prevOpt = getTcpOption(prevOptionType); + if (prevOpt.isNull()) + { + PCPP_LOG_ERROR("Previous option of type " << (int)prevOptionType << " not found, cannot add a new TCP option"); + return TcpOption(nullptr); + } + + offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data; + } + + return addTcpOptionAt(optionBuilder, offset); +} + +bool TcpLayer::removeTcpOption(TcpOptionType optionType) +{ + TcpOption opt = getTcpOption(optionType); + if (opt.isNull()) + { + return false; + } + + // calculate total TCP option size + TcpOption curOpt = getFirstTcpOption(); + size_t totalOptSize = 0; + while (!curOpt.isNull()) + { + totalOptSize += curOpt.getTotalSize(); + curOpt = getNextTcpOption(curOpt); + } + totalOptSize -= opt.getTotalSize(); + + + int offset = opt.getRecordBasePtr() - m_Data; + + if (!shortenLayer(offset, opt.getTotalSize())) + { + return false; + } + + adjustTcpOptionTrailer(totalOptSize); + + m_OptionReader.changeTLVRecordCount(-1); + + return true; +} + +bool TcpLayer::removeAllTcpOptions() +{ + int offset = sizeof(tcphdr); + + if (!shortenLayer(offset, getHeaderLen()-offset)) + return false; + + getTcpHeader()->dataOffset = sizeof(tcphdr)/4; + m_NumOfTrailingBytes = 0; + m_OptionReader.changeTLVRecordCount(0-getTcpOptionCount()); + return true; +} + +TcpOption TcpLayer::addTcpOptionAt(const TcpOptionBuilder& optionBuilder, int offset) +{ + TcpOption newOption = optionBuilder.build(); + if (newOption.isNull()) + return newOption; + + // calculate total TCP option size + TcpOption curOpt = getFirstTcpOption(); + size_t totalOptSize = 0; + while (!curOpt.isNull()) + { + totalOptSize += curOpt.getTotalSize(); + curOpt = getNextTcpOption(curOpt); + } + totalOptSize += newOption.getTotalSize(); + + size_t sizeToExtend = newOption.getTotalSize(); + + if (!extendLayer(offset, sizeToExtend)) + { + PCPP_LOG_ERROR("Could not extend TcpLayer in [" << sizeToExtend << "] bytes"); + newOption.purgeRecordData(); + return TcpOption(nullptr); + } + + memcpy(m_Data + offset, newOption.getRecordBasePtr(), newOption.getTotalSize()); + + newOption.purgeRecordData(); + + adjustTcpOptionTrailer(totalOptSize); + + m_OptionReader.changeTLVRecordCount(1); + + uint8_t* newOptPtr = m_Data + offset; + + return TcpOption(newOptPtr); +} + +void TcpLayer::adjustTcpOptionTrailer(size_t totalOptSize) +{ + int newNumberOfTrailingBytes = 0; + while ((totalOptSize + newNumberOfTrailingBytes) % 4 != 0) + newNumberOfTrailingBytes++; + + if (newNumberOfTrailingBytes < m_NumOfTrailingBytes) + shortenLayer(sizeof(tcphdr)+totalOptSize, m_NumOfTrailingBytes - newNumberOfTrailingBytes - 1); + else if (newNumberOfTrailingBytes > m_NumOfTrailingBytes) + extendLayer(sizeof(tcphdr)+totalOptSize, newNumberOfTrailingBytes - m_NumOfTrailingBytes); + + m_NumOfTrailingBytes = newNumberOfTrailingBytes; + + for (int i = 0; i < m_NumOfTrailingBytes; i++) + m_Data[sizeof(tcphdr) + totalOptSize + i] = TCPOPT_DUMMY; + + getTcpHeader()->dataOffset = (sizeof(tcphdr) + totalOptSize + m_NumOfTrailingBytes)/4; +} + +uint16_t TcpLayer::calculateChecksum(bool writeResultToPacket) +{ + tcphdr* tcpHdr = getTcpHeader(); + uint16_t checksumRes = 0; + uint16_t currChecksumValue = tcpHdr->headerChecksum; + + if (m_PrevLayer != nullptr) + { + tcpHdr->headerChecksum = 0; + ScalarBuffer vec[2]; + PCPP_LOG_DEBUG("data len = " << m_DataLen); + vec[0].buffer = (uint16_t*)m_Data; + vec[0].len = m_DataLen; + + if (m_PrevLayer->getProtocol() == IPv4) + { + uint32_t srcIP = ((IPv4Layer*)m_PrevLayer)->getSrcIPv4Address().toInt(); + uint32_t dstIP = ((IPv4Layer*)m_PrevLayer)->getDstIPv4Address().toInt(); + uint16_t pseudoHeader[6]; + pseudoHeader[0] = srcIP >> 16; + pseudoHeader[1] = srcIP & 0xFFFF; + pseudoHeader[2] = dstIP >> 16; + pseudoHeader[3] = dstIP & 0xFFFF; + pseudoHeader[4] = 0xffff & htobe16(m_DataLen); + pseudoHeader[5] = htobe16(0x00ff & PACKETPP_IPPROTO_TCP); + vec[1].buffer = pseudoHeader; + vec[1].len = 12; + checksumRes = computeChecksum(vec, 2); + PCPP_LOG_DEBUG("calculated checksum = 0x" << std::uppercase << std::hex << checksumRes); + + + } + else if (m_PrevLayer->getProtocol() == IPv6) + { + uint16_t pseudoHeader[18]; + ((IPv6Layer*)m_PrevLayer)->getSrcIPv6Address().copyTo((uint8_t*)pseudoHeader); + ((IPv6Layer*)m_PrevLayer)->getDstIPv6Address().copyTo((uint8_t*)(pseudoHeader+8)); + pseudoHeader[16] = 0xffff & htobe16(m_DataLen); + pseudoHeader[17] = htobe16(0x00ff & PACKETPP_IPPROTO_TCP); + vec[1].buffer = pseudoHeader; + vec[1].len = 36; + checksumRes = computeChecksum(vec, 2); + PCPP_LOG_DEBUG("calculated checksum = 0xX" << std::uppercase << std::hex << checksumRes); + } + } + + if(writeResultToPacket) + tcpHdr->headerChecksum = htobe16(checksumRes); + else + tcpHdr->headerChecksum = currChecksumValue; + + return checksumRes; +} + +void TcpLayer::initLayer() +{ + m_DataLen = sizeof(tcphdr); + m_Data = new uint8_t[m_DataLen]; + memset(m_Data, 0, m_DataLen); + m_Protocol = TCP; + m_NumOfTrailingBytes = 0; + getTcpHeader()->dataOffset = sizeof(tcphdr)/4; +} + +TcpLayer::TcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet) +{ + m_Protocol = TCP; + m_NumOfTrailingBytes = 0; +} + +TcpLayer::TcpLayer() +{ + initLayer(); +} + +TcpLayer::TcpLayer(uint16_t portSrc, uint16_t portDst) +{ + initLayer(); + getTcpHeader()->portDst = htobe16(portDst); + getTcpHeader()->portSrc = htobe16(portSrc); +} + +void TcpLayer::copyLayerData(const TcpLayer& other) +{ + m_OptionReader = other.m_OptionReader; + m_NumOfTrailingBytes = other.m_NumOfTrailingBytes; +} + +TcpLayer::TcpLayer(const TcpLayer& other) : Layer(other) +{ + copyLayerData(other); +} + +TcpLayer& TcpLayer::operator=(const TcpLayer& other) +{ + Layer::operator=(other); + + copyLayerData(other); + + return *this; +} + +void TcpLayer::parseNextLayer() +{ + size_t headerLen = getHeaderLen(); + if (m_DataLen <= headerLen) + return; + + uint8_t* payload = m_Data + headerLen; + size_t payloadLen = m_DataLen - headerLen; + uint16_t portDst = getDstPort(); + uint16_t portSrc = getSrcPort(); + + if (HttpMessage::isHttpPort(portDst) && HttpRequestFirstLine::parseMethod((char*)payload, payloadLen) != HttpRequestLayer::HttpMethodUnknown) + m_NextLayer = new HttpRequestLayer(payload, payloadLen, this, m_Packet); + else if (HttpMessage::isHttpPort(portSrc) && HttpResponseFirstLine::parseStatusCode((char*)payload, payloadLen) != HttpResponseLayer::HttpStatusCodeUnknown) + m_NextLayer = new HttpResponseLayer(payload, payloadLen, this, m_Packet); + else if (SSLLayer::IsSSLMessage(portSrc, portDst, payload, payloadLen)) + m_NextLayer = SSLLayer::createSSLMessage(payload, payloadLen, this, m_Packet); + else if (SipLayer::isSipPort(portDst) || SipLayer::isSipPort(portSrc)) + { + if (SipRequestFirstLine::parseMethod((char*)payload, payloadLen) != SipRequestLayer::SipMethodUnknown) + m_NextLayer = new SipRequestLayer(payload, payloadLen, this, m_Packet); + else if (SipResponseFirstLine::parseStatusCode((char*)payload, payloadLen) != SipResponseLayer::SipStatusCodeUnknown) + m_NextLayer = new SipResponseLayer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } + else if (BgpLayer::isBgpPort(portSrc, portDst)) + { + m_NextLayer = BgpLayer::parseBgpLayer(payload, payloadLen, this, m_Packet); + if (!m_NextLayer) + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + } + else if (SSHLayer::isSSHPort(portSrc, portDst)) + m_NextLayer = SSHLayer::createSSHMessage(payload, payloadLen, this, m_Packet); + else if (DnsLayer::isDataValid(payload, payloadLen, true) && (DnsLayer::isDnsPort(portDst) || DnsLayer::isDnsPort(portSrc))) + m_NextLayer = new DnsOverTcpLayer(payload, payloadLen, this, m_Packet); + else if (TelnetLayer::isDataValid(payload, payloadLen) && (TelnetLayer::isTelnetPort(portDst) || TelnetLayer::isTelnetPort(portSrc))) + m_NextLayer = new TelnetLayer(payload, payloadLen, this, m_Packet); + else if (FtpLayer::isFtpPort(portSrc) && FtpLayer::isDataValid(payload, payloadLen)) + m_NextLayer = new FtpResponseLayer(payload, payloadLen, this, m_Packet); + else if (FtpLayer::isFtpPort(portDst) && FtpLayer::isDataValid(payload, payloadLen)) + m_NextLayer = new FtpRequestLayer(payload, payloadLen, this, m_Packet); + else if (SomeIpLayer::isSomeIpPort(portSrc) || SomeIpLayer::isSomeIpPort(portDst)) + m_NextLayer = SomeIpLayer::parseSomeIpLayer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); +} + +void TcpLayer::computeCalculateFields() +{ + tcphdr* tcpHdr = getTcpHeader(); + + tcpHdr->dataOffset = getHeaderLen() >> 2; + calculateChecksum(true); +} + +std::string TcpLayer::toString() const +{ + tcphdr* hdr = getTcpHeader(); + std::string result = "TCP Layer, "; + if (hdr->synFlag) + { + if (hdr->ackFlag) + result += "[SYN, ACK], "; + else + result += "[SYN], "; + } + else if (hdr->finFlag) + { + if (hdr->ackFlag) + result += "[FIN, ACK], "; + else + result += "[FIN], "; + } + else if (hdr->ackFlag) + result += "[ACK], "; + + std::ostringstream srcPortStream; + srcPortStream << getSrcPort(); + std::ostringstream dstPortStream; + dstPortStream << getDstPort(); + result += "Src port: " + srcPortStream.str() + ", Dst port: " + dstPortStream.str(); + + return result; +} + +} // namespace pcpp diff --git a/Packet++/src/TcpReassembly.cpp b/Packet++/src/TcpReassembly.cpp index c90bcc7cef..9642fb6f08 100644 --- a/Packet++/src/TcpReassembly.cpp +++ b/Packet++/src/TcpReassembly.cpp @@ -1,772 +1,772 @@ -#define LOG_MODULE PacketLogModuleTcpReassembly - -#include "TcpReassembly.h" -#include "TcpLayer.h" -#include "IPLayer.h" -#include "PacketUtils.h" -#include "Logger.h" -#include -#include -#include "EndianPortable.h" -#include "TimespecTimeval.h" -#ifdef _MSC_VER -#include -#endif - -#define PURGE_FREQ_SECS 1 - -#define SEQ_LT(a,b) ((int32_t)((a)-(b)) < 0) -#define SEQ_LEQ(a,b) ((int32_t)((a)-(b)) <= 0) -#define SEQ_GT(a,b) ((int32_t)((a)-(b)) > 0) -#define SEQ_GEQ(a,b) ((int32_t)((a)-(b)) >= 0) - -namespace pcpp -{ - -static timeval timespecToTimeval(const timespec& in) -{ - timeval out; - TIMESPEC_TO_TIMEVAL(&out, &in); - return out; -} - - -TcpReassembly::TcpReassembly(OnTcpMessageReady onMessageReadyCallback, void* userCookie, OnTcpConnectionStart onConnectionStartCallback, OnTcpConnectionEnd onConnectionEndCallback, const TcpReassemblyConfiguration &config) -{ - m_OnMessageReadyCallback = onMessageReadyCallback; - m_UserCookie = userCookie; - m_OnConnStart = onConnectionStartCallback; - m_OnConnEnd = onConnectionEndCallback; - m_ClosedConnectionDelay = (config.closedConnectionDelay > 0) ? config.closedConnectionDelay : 5; - m_RemoveConnInfo = config.removeConnInfo; - m_MaxNumToClean = (config.removeConnInfo == true && config.maxNumToClean == 0) ? 30 : config.maxNumToClean; - m_MaxOutOfOrderFragments = config.maxOutOfOrderFragments; - m_PurgeTimepoint = time(nullptr) + PURGE_FREQ_SECS; - m_EnableBaseBufferClearCondition = config.enableBaseBufferClearCondition; -} - - -TcpReassembly::ReassemblyStatus TcpReassembly::reassemblePacket(Packet& tcpData) -{ - // automatic cleanup - if (m_RemoveConnInfo == true) - { - if (time(nullptr) >= m_PurgeTimepoint) - { - purgeClosedConnections(); - m_PurgeTimepoint = time(nullptr) + PURGE_FREQ_SECS; - } - } - - - // calculate packet's source and dest IP address - IPAddress srcIP, dstIP; - - if (tcpData.isPacketOfType(IP)) - { - const IPLayer* ipLayer = tcpData.getLayerOfType(); - srcIP = ipLayer->getSrcIPAddress(); - dstIP = ipLayer->getDstIPAddress(); - } - else - return NonIpPacket; - - // in real traffic the IP addresses cannot be an unspecified - if (!srcIP.isValid() || !dstIP.isValid()) - return NonIpPacket; - - - // Ignore non-TCP packets - TcpLayer* tcpLayer = tcpData.getLayerOfType(true); // lookup in reverse order - if (tcpLayer == nullptr) - { - return NonTcpPacket; - } - - // Ignore the packet if it's an ICMP packet that has a TCP layer - // Several ICMP messages (like "destination unreachable") have TCP data as part of the ICMP message. - // This is not real TCP data and packet can be ignored - if (tcpData.isPacketOfType(ICMP)) - { - PCPP_LOG_DEBUG("Packet is of type ICMP so TCP data is probably part of the ICMP message. Ignoring this packet"); - return NonTcpPacket; - } - - ReassemblyStatus status = TcpMessageHandled; - - // set the TCP payload size - size_t tcpPayloadSize = tcpLayer->getLayerPayloadSize(); - - // calculate if this packet has FIN or RST flags - bool isFin = (tcpLayer->getTcpHeader()->finFlag == 1); - bool isRst = (tcpLayer->getTcpHeader()->rstFlag == 1); - bool isFinOrRst = isFin || isRst; - - // ignore ACK packets or TCP packets with no payload (except for SYN, FIN or RST packets which we'll later need) - if (tcpPayloadSize == 0 && tcpLayer->getTcpHeader()->synFlag == 0 && !isFinOrRst) - { - return Ignore_PacketWithNoData; - } - - TcpReassemblyData* tcpReassemblyData = nullptr; - - // calculate flow key for this packet - uint32_t flowKey = hash5Tuple(&tcpData); - - // time stamp for this packet - timeval currTime = timespecToTimeval(tcpData.getRawPacket()->getPacketTimeStamp()); - - // find the connection in the connection map - ConnectionList::iterator iter = m_ConnectionList.find(flowKey); - - if (iter == m_ConnectionList.end()) - { - // if it's a packet of a new connection, create a TcpReassemblyData object and add it to the active connection list - std::pair pair = m_ConnectionList.insert(std::make_pair(flowKey, TcpReassemblyData())); - tcpReassemblyData = &pair.first->second; - tcpReassemblyData->connData.srcIP = srcIP; - tcpReassemblyData->connData.dstIP = dstIP; - tcpReassemblyData->connData.srcPort = tcpLayer->getSrcPort(); - tcpReassemblyData->connData.dstPort = tcpLayer->getDstPort(); - tcpReassemblyData->connData.flowKey = flowKey; - tcpReassemblyData->connData.setStartTime(currTime); - - m_ConnectionInfo[flowKey] = tcpReassemblyData->connData; - - // fire connection start callback - if (m_OnConnStart != nullptr) - m_OnConnStart(tcpReassemblyData->connData, m_UserCookie); - } - else // connection already exists - { - // if this packet belongs to a connection that was already closed (for example: data packet that comes after FIN), ignore it. - if (iter->second.closed) - { - PCPP_LOG_DEBUG("Ignoring packet of already closed flow [0x" << std::hex << flowKey << "]"); - return Ignore_PacketOfClosedFlow; - } - - tcpReassemblyData = &iter->second; - - if (currTime.tv_sec > tcpReassemblyData->connData.endTime.tv_sec) - { - tcpReassemblyData->connData.setEndTime(currTime); - m_ConnectionInfo[flowKey].setEndTime(currTime); - } - else if (currTime.tv_sec == tcpReassemblyData->connData.endTime.tv_sec) - { - if (currTime.tv_usec > tcpReassemblyData->connData.endTime.tv_usec) - { - tcpReassemblyData->connData.setEndTime(currTime); - m_ConnectionInfo[flowKey].setEndTime(currTime); - } - } - } - - timeval timestampOfTheReceivedPacket = currTime; - int8_t sideIndex = -1; - bool first = false; - - // calculate packet's source port - uint16_t srcPort = tcpLayer->getTcpHeader()->portSrc; - - // if this is a new connection and it's the first packet we see on that connection - if (tcpReassemblyData->numOfSides == 0) - { - PCPP_LOG_DEBUG("Setting side for new connection"); - - // open the first side of the connection, side index is 0 - sideIndex = 0; - tcpReassemblyData->twoSides[sideIndex].srcIP = srcIP; - tcpReassemblyData->twoSides[sideIndex].srcPort = srcPort; - tcpReassemblyData->numOfSides++; - first = true; - } - // if there is already one side in this connection (which will be at side index 0) - else if (tcpReassemblyData->numOfSides == 1) - { - // check if packet belongs to that side - if (tcpReassemblyData->twoSides[0].srcPort == srcPort && tcpReassemblyData->twoSides[0].srcIP == srcIP) - { - sideIndex = 0; - } - else - { - // this means packet belong to the second side which doesn't yet exist. Open a second side with side index 1 - PCPP_LOG_DEBUG("Setting second side of a connection"); - sideIndex = 1; - tcpReassemblyData->twoSides[sideIndex].srcIP = srcIP; - tcpReassemblyData->twoSides[sideIndex].srcPort = srcPort; - tcpReassemblyData->numOfSides++; - first = true; - } - } - // if there are already 2 sides open for this connection - else if (tcpReassemblyData->numOfSides == 2) - { - // check if packet matches side 0 - if (tcpReassemblyData->twoSides[0].srcPort == srcPort && tcpReassemblyData->twoSides[0].srcIP == srcIP) - { - sideIndex = 0; - } - // check if packet matches side 1 - else if (tcpReassemblyData->twoSides[1].srcPort == srcPort && tcpReassemblyData->twoSides[1].srcIP == srcIP) - { - sideIndex = 1; - } - // packet doesn't match either side. This case doesn't make sense but it's handled anyway. Packet will be ignored - else - { - PCPP_LOG_ERROR("Error occurred - packet doesn't match either side of the connection!!"); - return Error_PacketDoesNotMatchFlow; - } - } - // there are more than 2 side - this case doesn't make sense and shouldn't happen, but handled anyway. Packet will be ignored - else - { - PCPP_LOG_ERROR("Error occurred - connection has more than 2 sides!!"); - return Error_PacketDoesNotMatchFlow; - } - - // if this side already got FIN or RST packet before, ignore this packet as this side is considered closed - if (tcpReassemblyData->twoSides[sideIndex].gotFinOrRst) - { - PCPP_LOG_DEBUG("Got a packet after FIN or RST were already seen on this side (" << sideIndex << "). Ignoring this packet"); - return Ignore_PacketOfClosedFlow; - } - - // handle FIN/RST packets that don't contain additional TCP data - if (isFinOrRst && tcpPayloadSize == 0) - { - PCPP_LOG_DEBUG("Got FIN or RST packet without data on side " << sideIndex); - - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - return FIN_RSTWithNoData; - } - - // check if this packet contains data from a different side than the side seen before. - // If this is the case then treat the out-of-order packet list as missing data and send them to the user (callback) together with an indication that some data was missing. - // Why? because a new packet from the other side means the previous message was probably already received and a new message is starting. - // In this case out-of-order packets are probably actually missing data - // For example: let's assume these are HTTP messages. If we're seeing the first packet of a response this means the server has already received the full request and is now starting - // to send the response. So if we still have out-of-order packets from the request it probably means that some packets were lost during the capture. So we don't expect the client to - // continue sending packets of the previous request, so we'll treat the out-of-order packets as missing data - // - // I'm aware that there are edge cases where the situation I described above is not true, but at some point we must clean the out-of-order packet list to avoid memory leak. - // I decided to do what Wireshark does and clean this list when starting to see a message from the other side - - // Since there are instances where this buffer clear condition can lead to declaration of excessive missing packets. Hence user should have a config file parameter - // to disable this and purely rely on max buffer size condition. As none of them are perfect solutions this will give user a little more control over it. - - if (m_EnableBaseBufferClearCondition && !first && tcpPayloadSize > 0 && tcpReassemblyData->prevSide != -1 && tcpReassemblyData->prevSide != sideIndex && - tcpReassemblyData->twoSides[tcpReassemblyData->prevSide].tcpFragmentList.size() > 0) - { - PCPP_LOG_DEBUG("Seeing a first data packet from a different side. Previous side was " << tcpReassemblyData->prevSide << ", current side is " << sideIndex); - checkOutOfOrderFragments(tcpReassemblyData, tcpReassemblyData->prevSide, true); - } - tcpReassemblyData->prevSide = sideIndex; - - // extract sequence value from packet - uint32_t sequence = be32toh(tcpLayer->getTcpHeader()->sequenceNumber); - - // if it's the first packet we see on this side of the connection - if (first) - { - PCPP_LOG_DEBUG("First data from this side of the connection"); - - // set initial sequence - tcpReassemblyData->twoSides[sideIndex].sequence = sequence + tcpPayloadSize; - if (tcpLayer->getTcpHeader()->synFlag != 0) - tcpReassemblyData->twoSides[sideIndex].sequence++; - - // send data to the callback - if (tcpPayloadSize != 0 && m_OnMessageReadyCallback != nullptr) - { - TcpStreamData streamData(tcpLayer->getLayerPayload(), tcpPayloadSize, 0, tcpReassemblyData->connData, timestampOfTheReceivedPacket); - m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); - } - status = TcpMessageHandled; - - // handle case where this packet is FIN or RST (although it's unlikely) - if (isFinOrRst) - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - - // return - nothing else to do here - return status; - } - - // if packet sequence is smaller than expected - this means that part or all of the TCP data is being re-transmitted - if (SEQ_LT(sequence, tcpReassemblyData->twoSides[sideIndex].sequence)) - { - PCPP_LOG_DEBUG("Found new data with the sequence lower than expected"); - - // calculate the sequence after this packet to see if this TCP payload contains also new data - uint32_t newSequence = sequence + tcpPayloadSize; - - // this means that some of payload is new - if (SEQ_GT(newSequence, tcpReassemblyData->twoSides[sideIndex].sequence)) - { - // calculate the size of the new data - uint32_t newLength = tcpReassemblyData->twoSides[sideIndex].sequence - sequence; - - PCPP_LOG_DEBUG("Although sequence is lower than expected payload is long enough to contain new data. Calling the callback with the new data"); - - // update the sequence for this side to include the new data that was seen - tcpReassemblyData->twoSides[sideIndex].sequence += tcpPayloadSize - newLength; - - // send only the new data to the callback - if (m_OnMessageReadyCallback != nullptr) - { - TcpStreamData streamData(tcpLayer->getLayerPayload() + newLength, tcpPayloadSize - newLength, 0, tcpReassemblyData->connData, timestampOfTheReceivedPacket); - m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); - } - status = TcpMessageHandled; - } - else - { - status = Ignore_Retransimission; - } - - // handle case where this packet is FIN or RST - if (isFinOrRst) - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - - // return - nothing else to do here - return status; - } - - // if packet sequence is exactly as expected - this is the "good" case and the most common one - else if (sequence == tcpReassemblyData->twoSides[sideIndex].sequence) - { - // if TCP data size is 0 - nothing to do - if (tcpPayloadSize == 0) - { - PCPP_LOG_DEBUG("Payload length is 0, doing nothing"); - - // handle case where this packet is FIN or RST - if (isFinOrRst) - { - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - status = FIN_RSTWithNoData; - } - else - { - status = Ignore_PacketWithNoData; - } - - return status; - } - - PCPP_LOG_DEBUG("Found new data with expected sequence. Calling the callback"); - - // update the sequence for this side to include TCP data from this packet - tcpReassemblyData->twoSides[sideIndex].sequence += tcpPayloadSize; - - // if this is a SYN packet - add +1 to the sequence - if (tcpLayer->getTcpHeader()->synFlag != 0) - tcpReassemblyData->twoSides[sideIndex].sequence++; - - // send the data to the callback - if (m_OnMessageReadyCallback != nullptr) - { - TcpStreamData streamData(tcpLayer->getLayerPayload(), tcpPayloadSize, 0, tcpReassemblyData->connData,timestampOfTheReceivedPacket); - m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); - } - status = TcpMessageHandled; - - // now that we've seen new data, go over the list of out-of-order packets and see if one or more of them fits now - checkOutOfOrderFragments(tcpReassemblyData, sideIndex, false); - - // handle case where this packet is FIN or RST - if (isFinOrRst) - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - - // return - nothing else to do here - return status; - } - - // this case means sequence size of the packet is higher than expected which means the packet is out-of-order or some packets were lost (missing data). - // we don't know which of the 2 cases it is at this point so we just add this data to the out-of-order packet list - else - { - // if TCP data size is 0 - nothing to do - if (tcpPayloadSize == 0) - { - PCPP_LOG_DEBUG("Payload length is 0, doing nothing"); - - // handle case where this packet is FIN or RST - if (isFinOrRst) - { - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - status = FIN_RSTWithNoData; - } - else - { - status = Ignore_PacketWithNoData; - } - - return status; - } - - // create a new TcpFragment, copy the TCP data to it and add this packet to the the out-of-order packet list - TcpFragment* newTcpFrag = new TcpFragment(); - newTcpFrag->data = new uint8_t[tcpPayloadSize]; - newTcpFrag->dataLength = tcpPayloadSize; - newTcpFrag->sequence = sequence; - newTcpFrag->timestamp = timestampOfTheReceivedPacket; - memcpy(newTcpFrag->data, tcpLayer->getLayerPayload(), tcpPayloadSize); - tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.pushBack(newTcpFrag); - - PCPP_LOG_DEBUG("Found out-of-order packet and added a new TCP fragment with size " << tcpPayloadSize << " to the out-of-order list of side " << sideIndex); - status = OutOfOrderTcpMessageBuffered; - - // check if we've stored too many out-of-order fragments; if so, consider missing packets lost and - // continue processing until the number of stored fragments is lower than the acceptable limit again - if (m_MaxOutOfOrderFragments > 0 && tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size() > m_MaxOutOfOrderFragments) - { - checkOutOfOrderFragments(tcpReassemblyData, sideIndex, false); - } - - // handle case where this packet is FIN or RST - if (isFinOrRst) - { - handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); - } - - return status; - } -} - -TcpReassembly::ReassemblyStatus TcpReassembly::reassemblePacket(RawPacket* tcpRawData) -{ - Packet parsedPacket(tcpRawData, false); - return reassemblePacket(parsedPacket); -} - -static std::string prepareMissingDataMessage(uint32_t missingDataLen) -{ - std::stringstream missingDataTextStream; - missingDataTextStream << '[' << missingDataLen << " bytes missing]"; - return missingDataTextStream.str(); -} - -void TcpReassembly::handleFinOrRst(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, uint32_t flowKey, bool isRst) -{ - // if this side already saw a FIN or RST packet, do nothing and return - if (tcpReassemblyData->twoSides[sideIndex].gotFinOrRst) - return; - - PCPP_LOG_DEBUG("Handling FIN or RST packet on side " << sideIndex); - - // set FIN/RST flag for this side - tcpReassemblyData->twoSides[sideIndex].gotFinOrRst = true; - - // check if the other side also sees FIN or RST packet. If so - just close the flow. Otherwise - clear the out-of-order packets for this side - int otherSideIndex = 1 - sideIndex; - if (tcpReassemblyData->twoSides[otherSideIndex].gotFinOrRst) - { - closeConnectionInternal(flowKey, TcpReassembly::TcpReassemblyConnectionClosedByFIN_RST); - return; - } - else - checkOutOfOrderFragments(tcpReassemblyData, sideIndex, true); - - // and if it's a rst, close the flow unilaterally - if(isRst) - closeConnectionInternal(flowKey, TcpReassembly::TcpReassemblyConnectionClosedByFIN_RST); -} - -void TcpReassembly::checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, bool cleanWholeFragList) -{ - bool foundSomething = false; - - do - { - PCPP_LOG_DEBUG("Starting first iteration of checkOutOfOrderFragments - looking for fragments that match the current sequence or have smaller sequence"); - - int index = 0; - foundSomething = false; - - do - { - index = 0; - foundSomething = false; - - // first fragment list iteration - go over the whole fragment list and see if can find fragments that match the current sequence - // or have smaller sequence but have big enough payload to get new data - while (index < (int)tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size()) - { - TcpFragment* curTcpFrag = tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(index); - - // if fragment sequence matches the current sequence - if (curTcpFrag->sequence == tcpReassemblyData->twoSides[sideIndex].sequence) - { - // update sequence - tcpReassemblyData->twoSides[sideIndex].sequence += curTcpFrag->dataLength; - if (curTcpFrag->data != nullptr) - { - PCPP_LOG_DEBUG("Found an out-of-order packet matching to the current sequence with size " << curTcpFrag->dataLength << " on side " << sideIndex << ". Pulling it out of the list and sending the data to the callback"); - - // send new data to callback - - if (m_OnMessageReadyCallback != nullptr) - { - TcpStreamData streamData(curTcpFrag->data, curTcpFrag->dataLength, 0, tcpReassemblyData->connData, curTcpFrag->timestamp); - m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); - } - } - - - // remove fragment from list - tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase(tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + index); - - foundSomething = true; - - continue; - } - - // if fragment sequence has lower sequence than the current sequence - if (SEQ_LT(curTcpFrag->sequence, tcpReassemblyData->twoSides[sideIndex].sequence)) - { - // check if it still has new data - uint32_t newSequence = curTcpFrag->sequence + curTcpFrag->dataLength; - - // it has new data - if (SEQ_GT(newSequence, tcpReassemblyData->twoSides[sideIndex].sequence)) - { - // calculate the delta new data size - uint32_t newLength = tcpReassemblyData->twoSides[sideIndex].sequence - curTcpFrag->sequence; - - PCPP_LOG_DEBUG("Found a fragment in the out-of-order list which its sequence is lower than expected but its payload is long enough to contain new data. " - "Calling the callback with the new data. Fragment size is " << curTcpFrag->dataLength << " on side " << sideIndex << ", new data size is " << (int)(curTcpFrag->dataLength - newLength)); - - // update current sequence with the delta new data size - tcpReassemblyData->twoSides[sideIndex].sequence += curTcpFrag->dataLength - newLength; - - // send only the new data to the callback - if (m_OnMessageReadyCallback != nullptr) - { - TcpStreamData streamData(curTcpFrag->data + newLength, curTcpFrag->dataLength - newLength, 0, tcpReassemblyData->connData, curTcpFrag->timestamp); - m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); - } - - foundSomething = true; - } - else - { - PCPP_LOG_DEBUG("Found a fragment in the out-of-order list which doesn't contain any new data, ignoring it. Fragment size is " << curTcpFrag->dataLength << " on side " << sideIndex); - } - - // delete fragment from list - tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase(tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + index); - - continue; - } - - //if got to here it means the fragment has higher sequence than current sequence, increment index and continue - index++; - } - - // if managed to find new segment, do the search all over again - } while (foundSomething); - - - // if got here it means we're left only with fragments that have higher sequence than current sequence. This means out-of-order packets or - // missing data. If we don't want to clear the frag list yet and the number of out of order fragments isn't above the configured limit, - // assume it's out-of-order and return - if (!cleanWholeFragList && (m_MaxOutOfOrderFragments == 0 || tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size() <= m_MaxOutOfOrderFragments)) - { - return; - } - - PCPP_LOG_DEBUG("Starting second iteration of checkOutOfOrderFragments - handle missing data"); - - // second fragment list iteration - now we're left only with fragments that have higher sequence than current sequence. This means missing data. - // Search for the fragment with the closest sequence to the current one - - uint32_t closestSequence = 0xffffffff; - bool closestSequenceDefined = false; - int closestSequenceFragIndex = -1; - index = 0; - - while (index < (int)tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size()) - { - // extract segment at current index - TcpFragment* curTcpFrag = tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(index); - - // check if its sequence is closer than current closest sequence - if (!closestSequenceDefined || SEQ_LT(curTcpFrag->sequence, closestSequence)) - { - closestSequence = curTcpFrag->sequence; - closestSequenceFragIndex = index; - closestSequenceDefined = true; - } - - index++; - } - - // this means fragment list is not empty at this stage - if (closestSequenceFragIndex > -1) - { - // get the fragment with the closest sequence - TcpFragment* curTcpFrag = tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(closestSequenceFragIndex); - - // calculate number of missing bytes - uint32_t missingDataLen = curTcpFrag->sequence - tcpReassemblyData->twoSides[sideIndex].sequence; - - // update sequence - tcpReassemblyData->twoSides[sideIndex].sequence = curTcpFrag->sequence + curTcpFrag->dataLength; - if (curTcpFrag->data != nullptr) - { - // send new data to callback - if (m_OnMessageReadyCallback != nullptr) - { - // prepare missing data text - std::string missingDataTextStr = prepareMissingDataMessage(missingDataLen); - - // add missing data text to the data that will be sent to the callback. This means that the data will look something like: - // "[xx bytes missing]" - std::vector dataWithMissingDataText; - dataWithMissingDataText.reserve(missingDataTextStr.length() + curTcpFrag->dataLength); - dataWithMissingDataText.insert(dataWithMissingDataText.end(), missingDataTextStr.begin(), missingDataTextStr.end()); - dataWithMissingDataText.insert(dataWithMissingDataText.end(), curTcpFrag->data, curTcpFrag->data + curTcpFrag->dataLength); - - //TcpStreamData streamData(curTcpFrag->data, curTcpFrag->dataLength, tcpReassemblyData->connData); - TcpStreamData streamData(&dataWithMissingDataText[0], dataWithMissingDataText.size(), missingDataLen, tcpReassemblyData->connData, curTcpFrag->timestamp); - m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); - - PCPP_LOG_DEBUG("Found missing data on side " << sideIndex << ": " << missingDataLen << " byte are missing. Sending the closest fragment which is in size " << curTcpFrag->dataLength << " + missing text message which size is " << missingDataTextStr.length()); - } - } - - // remove fragment from list - tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase(tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + closestSequenceFragIndex); - - PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments again from the start"); - - // call the method again from the start to do the whole search again (both iterations). - // the stop condition is when the list is empty (so closestSequenceFragIndex == -1) - foundSomething = true; - } - - } while (foundSomething); -} - -void TcpReassembly::closeConnection(uint32_t flowKey) -{ - closeConnectionInternal(flowKey, TcpReassembly::TcpReassemblyConnectionClosedManually); -} - -void TcpReassembly::closeConnectionInternal(uint32_t flowKey, ConnectionEndReason reason) -{ - ConnectionList::iterator iter = m_ConnectionList.find(flowKey); - if (iter == m_ConnectionList.end()) - { - PCPP_LOG_ERROR("Cannot close flow with key 0x" << std::uppercase << std::hex << flowKey << ": cannot find flow"); - return; - } - - TcpReassemblyData& tcpReassemblyData = iter->second; - - if (tcpReassemblyData.closed) // the connection is already closed - return; - - PCPP_LOG_DEBUG("Closing connection with flow key 0x" << std::hex << flowKey); - - PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 0"); - checkOutOfOrderFragments(&tcpReassemblyData, 0, true); - - PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 1"); - checkOutOfOrderFragments(&tcpReassemblyData, 1, true); - - if (m_OnConnEnd != nullptr) - m_OnConnEnd(tcpReassemblyData.connData, reason, m_UserCookie); - - tcpReassemblyData.closed = true; // mark the connection as closed - insertIntoCleanupList(flowKey); - - PCPP_LOG_DEBUG("Connection with flow key 0x" << std::hex << flowKey << " is closed"); -} - -void TcpReassembly::closeAllConnections() -{ - PCPP_LOG_DEBUG("Closing all flows"); - - ConnectionList::iterator iter = m_ConnectionList.begin(), iterEnd = m_ConnectionList.end(); - for (; iter != iterEnd; ++iter) - { - TcpReassemblyData& tcpReassemblyData = iter->second; - - if (tcpReassemblyData.closed) // the connection is already closed, skip it - continue; - - uint32_t flowKey = tcpReassemblyData.connData.flowKey; - PCPP_LOG_DEBUG("Closing connection with flow key 0x" << std::hex << flowKey); - - PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 0"); - checkOutOfOrderFragments(&tcpReassemblyData, 0, true); - - PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 1"); - checkOutOfOrderFragments(&tcpReassemblyData, 1, true); - - if (m_OnConnEnd != nullptr) - m_OnConnEnd(tcpReassemblyData.connData, TcpReassemblyConnectionClosedManually, m_UserCookie); - - tcpReassemblyData.closed = true; // mark the connection as closed - insertIntoCleanupList(flowKey); - - PCPP_LOG_DEBUG("Connection with flow key 0x" << std::hex << flowKey << " is closed"); - } -} - -int TcpReassembly::isConnectionOpen(const ConnectionData& connection) const -{ - ConnectionList::const_iterator iter = m_ConnectionList.find(connection.flowKey); - if (iter != m_ConnectionList.end()) - return iter->second.closed == false; - - return -1; -} - -void TcpReassembly::insertIntoCleanupList(uint32_t flowKey) -{ - // m_CleanupList is a map with key of type time_t (expiration time). The mapped type is a list that stores the flow keys to be cleared in certain point of time. - // m_CleanupList.insert inserts an empty list if the container does not already contain an element with an equivalent key, - // otherwise this method returns an iterator to the element that prevents insertion. - std::pair pair = m_CleanupList.insert(std::make_pair(time(nullptr) + m_ClosedConnectionDelay, CleanupList::mapped_type())); - - // getting the reference to list - CleanupList::mapped_type& keysList = pair.first->second; - keysList.push_front(flowKey); -} - -uint32_t TcpReassembly::purgeClosedConnections(uint32_t maxNumToClean) -{ - uint32_t count = 0; - - if (maxNumToClean == 0) - maxNumToClean = m_MaxNumToClean; - - CleanupList::iterator iterTime = m_CleanupList.begin(), iterTimeEnd = m_CleanupList.upper_bound(time(nullptr)); - while (iterTime != iterTimeEnd && count < maxNumToClean) - { - CleanupList::mapped_type& keysList = iterTime->second; - - for (; !keysList.empty() && count < maxNumToClean; ++count) - { - CleanupList::mapped_type::const_reference key = keysList.front(); - m_ConnectionInfo.erase(key); - m_ConnectionList.erase(key); - keysList.pop_front(); - } - - if (keysList.empty()) - m_CleanupList.erase(iterTime++); - else - ++iterTime; - } - - return count; -} - -} +#define LOG_MODULE PacketLogModuleTcpReassembly + +#include "TcpReassembly.h" +#include "TcpLayer.h" +#include "IPLayer.h" +#include "PacketUtils.h" +#include "Logger.h" +#include +#include +#include "EndianPortable.h" +#include "TimespecTimeval.h" +#ifdef _MSC_VER +#include +#endif + +#define PURGE_FREQ_SECS 1 + +#define SEQ_LT(a,b) ((int32_t)((a)-(b)) < 0) +#define SEQ_LEQ(a,b) ((int32_t)((a)-(b)) <= 0) +#define SEQ_GT(a,b) ((int32_t)((a)-(b)) > 0) +#define SEQ_GEQ(a,b) ((int32_t)((a)-(b)) >= 0) + +namespace pcpp +{ + +static timeval timespecToTimeval(const timespec& in) +{ + timeval out; + TIMESPEC_TO_TIMEVAL(&out, &in); + return out; +} + + +TcpReassembly::TcpReassembly(OnTcpMessageReady onMessageReadyCallback, void* userCookie, OnTcpConnectionStart onConnectionStartCallback, OnTcpConnectionEnd onConnectionEndCallback, const TcpReassemblyConfiguration &config) +{ + m_OnMessageReadyCallback = onMessageReadyCallback; + m_UserCookie = userCookie; + m_OnConnStart = onConnectionStartCallback; + m_OnConnEnd = onConnectionEndCallback; + m_ClosedConnectionDelay = (config.closedConnectionDelay > 0) ? config.closedConnectionDelay : 5; + m_RemoveConnInfo = config.removeConnInfo; + m_MaxNumToClean = (config.removeConnInfo == true && config.maxNumToClean == 0) ? 30 : config.maxNumToClean; + m_MaxOutOfOrderFragments = config.maxOutOfOrderFragments; + m_PurgeTimepoint = time(nullptr) + PURGE_FREQ_SECS; + m_EnableBaseBufferClearCondition = config.enableBaseBufferClearCondition; +} + + +TcpReassembly::ReassemblyStatus TcpReassembly::reassemblePacket(Packet& tcpData) +{ + // automatic cleanup + if (m_RemoveConnInfo == true) + { + if (time(nullptr) >= m_PurgeTimepoint) + { + purgeClosedConnections(); + m_PurgeTimepoint = time(nullptr) + PURGE_FREQ_SECS; + } + } + + + // calculate packet's source and dest IP address + IPAddress srcIP, dstIP; + + if (tcpData.isPacketOfType(IP)) + { + const IPLayer* ipLayer = tcpData.getLayerOfType(); + srcIP = ipLayer->getSrcIPAddress(); + dstIP = ipLayer->getDstIPAddress(); + } + else + return NonIpPacket; + + // in real traffic the IP addresses cannot be an unspecified + if (!srcIP.isValid() || !dstIP.isValid()) + return NonIpPacket; + + + // Ignore non-TCP packets + TcpLayer* tcpLayer = tcpData.getLayerOfType(true); // lookup in reverse order + if (tcpLayer == nullptr) + { + return NonTcpPacket; + } + + // Ignore the packet if it's an ICMP packet that has a TCP layer + // Several ICMP messages (like "destination unreachable") have TCP data as part of the ICMP message. + // This is not real TCP data and packet can be ignored + if (tcpData.isPacketOfType(ICMP)) + { + PCPP_LOG_DEBUG("Packet is of type ICMP so TCP data is probably part of the ICMP message. Ignoring this packet"); + return NonTcpPacket; + } + + ReassemblyStatus status = TcpMessageHandled; + + // set the TCP payload size + size_t tcpPayloadSize = tcpLayer->getLayerPayloadSize(); + + // calculate if this packet has FIN or RST flags + bool isFin = (tcpLayer->getTcpHeader()->finFlag == 1); + bool isRst = (tcpLayer->getTcpHeader()->rstFlag == 1); + bool isFinOrRst = isFin || isRst; + + // ignore ACK packets or TCP packets with no payload (except for SYN, FIN or RST packets which we'll later need) + if (tcpPayloadSize == 0 && tcpLayer->getTcpHeader()->synFlag == 0 && !isFinOrRst) + { + return Ignore_PacketWithNoData; + } + + TcpReassemblyData* tcpReassemblyData = nullptr; + + // calculate flow key for this packet + uint32_t flowKey = hash5Tuple(&tcpData); + + // time stamp for this packet + timeval currTime = timespecToTimeval(tcpData.getRawPacket()->getPacketTimeStamp()); + + // find the connection in the connection map + ConnectionList::iterator iter = m_ConnectionList.find(flowKey); + + if (iter == m_ConnectionList.end()) + { + // if it's a packet of a new connection, create a TcpReassemblyData object and add it to the active connection list + std::pair pair = m_ConnectionList.insert(std::make_pair(flowKey, TcpReassemblyData())); + tcpReassemblyData = &pair.first->second; + tcpReassemblyData->connData.srcIP = srcIP; + tcpReassemblyData->connData.dstIP = dstIP; + tcpReassemblyData->connData.srcPort = tcpLayer->getSrcPort(); + tcpReassemblyData->connData.dstPort = tcpLayer->getDstPort(); + tcpReassemblyData->connData.flowKey = flowKey; + tcpReassemblyData->connData.setStartTime(currTime); + + m_ConnectionInfo[flowKey] = tcpReassemblyData->connData; + + // fire connection start callback + if (m_OnConnStart != nullptr) + m_OnConnStart(tcpReassemblyData->connData, m_UserCookie); + } + else // connection already exists + { + // if this packet belongs to a connection that was already closed (for example: data packet that comes after FIN), ignore it. + if (iter->second.closed) + { + PCPP_LOG_DEBUG("Ignoring packet of already closed flow [0x" << std::hex << flowKey << "]"); + return Ignore_PacketOfClosedFlow; + } + + tcpReassemblyData = &iter->second; + + if (currTime.tv_sec > tcpReassemblyData->connData.endTime.tv_sec) + { + tcpReassemblyData->connData.setEndTime(currTime); + m_ConnectionInfo[flowKey].setEndTime(currTime); + } + else if (currTime.tv_sec == tcpReassemblyData->connData.endTime.tv_sec) + { + if (currTime.tv_usec > tcpReassemblyData->connData.endTime.tv_usec) + { + tcpReassemblyData->connData.setEndTime(currTime); + m_ConnectionInfo[flowKey].setEndTime(currTime); + } + } + } + + timeval timestampOfTheReceivedPacket = currTime; + int8_t sideIndex = -1; + bool first = false; + + // calculate packet's source port + uint16_t srcPort = tcpLayer->getTcpHeader()->portSrc; + + // if this is a new connection and it's the first packet we see on that connection + if (tcpReassemblyData->numOfSides == 0) + { + PCPP_LOG_DEBUG("Setting side for new connection"); + + // open the first side of the connection, side index is 0 + sideIndex = 0; + tcpReassemblyData->twoSides[sideIndex].srcIP = srcIP; + tcpReassemblyData->twoSides[sideIndex].srcPort = srcPort; + tcpReassemblyData->numOfSides++; + first = true; + } + // if there is already one side in this connection (which will be at side index 0) + else if (tcpReassemblyData->numOfSides == 1) + { + // check if packet belongs to that side + if (tcpReassemblyData->twoSides[0].srcPort == srcPort && tcpReassemblyData->twoSides[0].srcIP == srcIP) + { + sideIndex = 0; + } + else + { + // this means packet belong to the second side which doesn't yet exist. Open a second side with side index 1 + PCPP_LOG_DEBUG("Setting second side of a connection"); + sideIndex = 1; + tcpReassemblyData->twoSides[sideIndex].srcIP = srcIP; + tcpReassemblyData->twoSides[sideIndex].srcPort = srcPort; + tcpReassemblyData->numOfSides++; + first = true; + } + } + // if there are already 2 sides open for this connection + else if (tcpReassemblyData->numOfSides == 2) + { + // check if packet matches side 0 + if (tcpReassemblyData->twoSides[0].srcPort == srcPort && tcpReassemblyData->twoSides[0].srcIP == srcIP) + { + sideIndex = 0; + } + // check if packet matches side 1 + else if (tcpReassemblyData->twoSides[1].srcPort == srcPort && tcpReassemblyData->twoSides[1].srcIP == srcIP) + { + sideIndex = 1; + } + // packet doesn't match either side. This case doesn't make sense but it's handled anyway. Packet will be ignored + else + { + PCPP_LOG_ERROR("Error occurred - packet doesn't match either side of the connection!!"); + return Error_PacketDoesNotMatchFlow; + } + } + // there are more than 2 side - this case doesn't make sense and shouldn't happen, but handled anyway. Packet will be ignored + else + { + PCPP_LOG_ERROR("Error occurred - connection has more than 2 sides!!"); + return Error_PacketDoesNotMatchFlow; + } + + // if this side already got FIN or RST packet before, ignore this packet as this side is considered closed + if (tcpReassemblyData->twoSides[sideIndex].gotFinOrRst) + { + PCPP_LOG_DEBUG("Got a packet after FIN or RST were already seen on this side (" << sideIndex << "). Ignoring this packet"); + return Ignore_PacketOfClosedFlow; + } + + // handle FIN/RST packets that don't contain additional TCP data + if (isFinOrRst && tcpPayloadSize == 0) + { + PCPP_LOG_DEBUG("Got FIN or RST packet without data on side " << sideIndex); + + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + return FIN_RSTWithNoData; + } + + // check if this packet contains data from a different side than the side seen before. + // If this is the case then treat the out-of-order packet list as missing data and send them to the user (callback) together with an indication that some data was missing. + // Why? because a new packet from the other side means the previous message was probably already received and a new message is starting. + // In this case out-of-order packets are probably actually missing data + // For example: let's assume these are HTTP messages. If we're seeing the first packet of a response this means the server has already received the full request and is now starting + // to send the response. So if we still have out-of-order packets from the request it probably means that some packets were lost during the capture. So we don't expect the client to + // continue sending packets of the previous request, so we'll treat the out-of-order packets as missing data + // + // I'm aware that there are edge cases where the situation I described above is not true, but at some point we must clean the out-of-order packet list to avoid memory leak. + // I decided to do what Wireshark does and clean this list when starting to see a message from the other side + + // Since there are instances where this buffer clear condition can lead to declaration of excessive missing packets. Hence user should have a config file parameter + // to disable this and purely rely on max buffer size condition. As none of them are perfect solutions this will give user a little more control over it. + + if (m_EnableBaseBufferClearCondition && !first && tcpPayloadSize > 0 && tcpReassemblyData->prevSide != -1 && tcpReassemblyData->prevSide != sideIndex && + tcpReassemblyData->twoSides[tcpReassemblyData->prevSide].tcpFragmentList.size() > 0) + { + PCPP_LOG_DEBUG("Seeing a first data packet from a different side. Previous side was " << tcpReassemblyData->prevSide << ", current side is " << sideIndex); + checkOutOfOrderFragments(tcpReassemblyData, tcpReassemblyData->prevSide, true); + } + tcpReassemblyData->prevSide = sideIndex; + + // extract sequence value from packet + uint32_t sequence = be32toh(tcpLayer->getTcpHeader()->sequenceNumber); + + // if it's the first packet we see on this side of the connection + if (first) + { + PCPP_LOG_DEBUG("First data from this side of the connection"); + + // set initial sequence + tcpReassemblyData->twoSides[sideIndex].sequence = sequence + tcpPayloadSize; + if (tcpLayer->getTcpHeader()->synFlag != 0) + tcpReassemblyData->twoSides[sideIndex].sequence++; + + // send data to the callback + if (tcpPayloadSize != 0 && m_OnMessageReadyCallback != nullptr) + { + TcpStreamData streamData(tcpLayer->getLayerPayload(), tcpPayloadSize, 0, tcpReassemblyData->connData, timestampOfTheReceivedPacket); + m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); + } + status = TcpMessageHandled; + + // handle case where this packet is FIN or RST (although it's unlikely) + if (isFinOrRst) + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + + // return - nothing else to do here + return status; + } + + // if packet sequence is smaller than expected - this means that part or all of the TCP data is being re-transmitted + if (SEQ_LT(sequence, tcpReassemblyData->twoSides[sideIndex].sequence)) + { + PCPP_LOG_DEBUG("Found new data with the sequence lower than expected"); + + // calculate the sequence after this packet to see if this TCP payload contains also new data + uint32_t newSequence = sequence + tcpPayloadSize; + + // this means that some of payload is new + if (SEQ_GT(newSequence, tcpReassemblyData->twoSides[sideIndex].sequence)) + { + // calculate the size of the new data + uint32_t newLength = tcpReassemblyData->twoSides[sideIndex].sequence - sequence; + + PCPP_LOG_DEBUG("Although sequence is lower than expected payload is long enough to contain new data. Calling the callback with the new data"); + + // update the sequence for this side to include the new data that was seen + tcpReassemblyData->twoSides[sideIndex].sequence += tcpPayloadSize - newLength; + + // send only the new data to the callback + if (m_OnMessageReadyCallback != nullptr) + { + TcpStreamData streamData(tcpLayer->getLayerPayload() + newLength, tcpPayloadSize - newLength, 0, tcpReassemblyData->connData, timestampOfTheReceivedPacket); + m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); + } + status = TcpMessageHandled; + } + else + { + status = Ignore_Retransimission; + } + + // handle case where this packet is FIN or RST + if (isFinOrRst) + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + + // return - nothing else to do here + return status; + } + + // if packet sequence is exactly as expected - this is the "good" case and the most common one + else if (sequence == tcpReassemblyData->twoSides[sideIndex].sequence) + { + // if TCP data size is 0 - nothing to do + if (tcpPayloadSize == 0) + { + PCPP_LOG_DEBUG("Payload length is 0, doing nothing"); + + // handle case where this packet is FIN or RST + if (isFinOrRst) + { + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + status = FIN_RSTWithNoData; + } + else + { + status = Ignore_PacketWithNoData; + } + + return status; + } + + PCPP_LOG_DEBUG("Found new data with expected sequence. Calling the callback"); + + // update the sequence for this side to include TCP data from this packet + tcpReassemblyData->twoSides[sideIndex].sequence += tcpPayloadSize; + + // if this is a SYN packet - add +1 to the sequence + if (tcpLayer->getTcpHeader()->synFlag != 0) + tcpReassemblyData->twoSides[sideIndex].sequence++; + + // send the data to the callback + if (m_OnMessageReadyCallback != nullptr) + { + TcpStreamData streamData(tcpLayer->getLayerPayload(), tcpPayloadSize, 0, tcpReassemblyData->connData,timestampOfTheReceivedPacket); + m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); + } + status = TcpMessageHandled; + + // now that we've seen new data, go over the list of out-of-order packets and see if one or more of them fits now + checkOutOfOrderFragments(tcpReassemblyData, sideIndex, false); + + // handle case where this packet is FIN or RST + if (isFinOrRst) + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + + // return - nothing else to do here + return status; + } + + // this case means sequence size of the packet is higher than expected which means the packet is out-of-order or some packets were lost (missing data). + // we don't know which of the 2 cases it is at this point so we just add this data to the out-of-order packet list + else + { + // if TCP data size is 0 - nothing to do + if (tcpPayloadSize == 0) + { + PCPP_LOG_DEBUG("Payload length is 0, doing nothing"); + + // handle case where this packet is FIN or RST + if (isFinOrRst) + { + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + status = FIN_RSTWithNoData; + } + else + { + status = Ignore_PacketWithNoData; + } + + return status; + } + + // create a new TcpFragment, copy the TCP data to it and add this packet to the the out-of-order packet list + TcpFragment* newTcpFrag = new TcpFragment(); + newTcpFrag->data = new uint8_t[tcpPayloadSize]; + newTcpFrag->dataLength = tcpPayloadSize; + newTcpFrag->sequence = sequence; + newTcpFrag->timestamp = timestampOfTheReceivedPacket; + memcpy(newTcpFrag->data, tcpLayer->getLayerPayload(), tcpPayloadSize); + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.pushBack(newTcpFrag); + + PCPP_LOG_DEBUG("Found out-of-order packet and added a new TCP fragment with size " << tcpPayloadSize << " to the out-of-order list of side " << sideIndex); + status = OutOfOrderTcpMessageBuffered; + + // check if we've stored too many out-of-order fragments; if so, consider missing packets lost and + // continue processing until the number of stored fragments is lower than the acceptable limit again + if (m_MaxOutOfOrderFragments > 0 && tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size() > m_MaxOutOfOrderFragments) + { + checkOutOfOrderFragments(tcpReassemblyData, sideIndex, false); + } + + // handle case where this packet is FIN or RST + if (isFinOrRst) + { + handleFinOrRst(tcpReassemblyData, sideIndex, flowKey, isRst); + } + + return status; + } +} + +TcpReassembly::ReassemblyStatus TcpReassembly::reassemblePacket(RawPacket* tcpRawData) +{ + Packet parsedPacket(tcpRawData, false); + return reassemblePacket(parsedPacket); +} + +static std::string prepareMissingDataMessage(uint32_t missingDataLen) +{ + std::stringstream missingDataTextStream; + missingDataTextStream << '[' << missingDataLen << " bytes missing]"; + return missingDataTextStream.str(); +} + +void TcpReassembly::handleFinOrRst(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, uint32_t flowKey, bool isRst) +{ + // if this side already saw a FIN or RST packet, do nothing and return + if (tcpReassemblyData->twoSides[sideIndex].gotFinOrRst) + return; + + PCPP_LOG_DEBUG("Handling FIN or RST packet on side " << sideIndex); + + // set FIN/RST flag for this side + tcpReassemblyData->twoSides[sideIndex].gotFinOrRst = true; + + // check if the other side also sees FIN or RST packet. If so - just close the flow. Otherwise - clear the out-of-order packets for this side + int otherSideIndex = 1 - sideIndex; + if (tcpReassemblyData->twoSides[otherSideIndex].gotFinOrRst) + { + closeConnectionInternal(flowKey, TcpReassembly::TcpReassemblyConnectionClosedByFIN_RST); + return; + } + else + checkOutOfOrderFragments(tcpReassemblyData, sideIndex, true); + + // and if it's a rst, close the flow unilaterally + if(isRst) + closeConnectionInternal(flowKey, TcpReassembly::TcpReassemblyConnectionClosedByFIN_RST); +} + +void TcpReassembly::checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, bool cleanWholeFragList) +{ + bool foundSomething = false; + + do + { + PCPP_LOG_DEBUG("Starting first iteration of checkOutOfOrderFragments - looking for fragments that match the current sequence or have smaller sequence"); + + int index = 0; + foundSomething = false; + + do + { + index = 0; + foundSomething = false; + + // first fragment list iteration - go over the whole fragment list and see if can find fragments that match the current sequence + // or have smaller sequence but have big enough payload to get new data + while (index < (int)tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size()) + { + TcpFragment* curTcpFrag = tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(index); + + // if fragment sequence matches the current sequence + if (curTcpFrag->sequence == tcpReassemblyData->twoSides[sideIndex].sequence) + { + // update sequence + tcpReassemblyData->twoSides[sideIndex].sequence += curTcpFrag->dataLength; + if (curTcpFrag->data != nullptr) + { + PCPP_LOG_DEBUG("Found an out-of-order packet matching to the current sequence with size " << curTcpFrag->dataLength << " on side " << sideIndex << ". Pulling it out of the list and sending the data to the callback"); + + // send new data to callback + + if (m_OnMessageReadyCallback != nullptr) + { + TcpStreamData streamData(curTcpFrag->data, curTcpFrag->dataLength, 0, tcpReassemblyData->connData, curTcpFrag->timestamp); + m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); + } + } + + + // remove fragment from list + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase(tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + index); + + foundSomething = true; + + continue; + } + + // if fragment sequence has lower sequence than the current sequence + if (SEQ_LT(curTcpFrag->sequence, tcpReassemblyData->twoSides[sideIndex].sequence)) + { + // check if it still has new data + uint32_t newSequence = curTcpFrag->sequence + curTcpFrag->dataLength; + + // it has new data + if (SEQ_GT(newSequence, tcpReassemblyData->twoSides[sideIndex].sequence)) + { + // calculate the delta new data size + uint32_t newLength = tcpReassemblyData->twoSides[sideIndex].sequence - curTcpFrag->sequence; + + PCPP_LOG_DEBUG("Found a fragment in the out-of-order list which its sequence is lower than expected but its payload is long enough to contain new data. " + "Calling the callback with the new data. Fragment size is " << curTcpFrag->dataLength << " on side " << sideIndex << ", new data size is " << (int)(curTcpFrag->dataLength - newLength)); + + // update current sequence with the delta new data size + tcpReassemblyData->twoSides[sideIndex].sequence += curTcpFrag->dataLength - newLength; + + // send only the new data to the callback + if (m_OnMessageReadyCallback != nullptr) + { + TcpStreamData streamData(curTcpFrag->data + newLength, curTcpFrag->dataLength - newLength, 0, tcpReassemblyData->connData, curTcpFrag->timestamp); + m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); + } + + foundSomething = true; + } + else + { + PCPP_LOG_DEBUG("Found a fragment in the out-of-order list which doesn't contain any new data, ignoring it. Fragment size is " << curTcpFrag->dataLength << " on side " << sideIndex); + } + + // delete fragment from list + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase(tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + index); + + continue; + } + + //if got to here it means the fragment has higher sequence than current sequence, increment index and continue + index++; + } + + // if managed to find new segment, do the search all over again + } while (foundSomething); + + + // if got here it means we're left only with fragments that have higher sequence than current sequence. This means out-of-order packets or + // missing data. If we don't want to clear the frag list yet and the number of out of order fragments isn't above the configured limit, + // assume it's out-of-order and return + if (!cleanWholeFragList && (m_MaxOutOfOrderFragments == 0 || tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size() <= m_MaxOutOfOrderFragments)) + { + return; + } + + PCPP_LOG_DEBUG("Starting second iteration of checkOutOfOrderFragments - handle missing data"); + + // second fragment list iteration - now we're left only with fragments that have higher sequence than current sequence. This means missing data. + // Search for the fragment with the closest sequence to the current one + + uint32_t closestSequence = 0xffffffff; + bool closestSequenceDefined = false; + int closestSequenceFragIndex = -1; + index = 0; + + while (index < (int)tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size()) + { + // extract segment at current index + TcpFragment* curTcpFrag = tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(index); + + // check if its sequence is closer than current closest sequence + if (!closestSequenceDefined || SEQ_LT(curTcpFrag->sequence, closestSequence)) + { + closestSequence = curTcpFrag->sequence; + closestSequenceFragIndex = index; + closestSequenceDefined = true; + } + + index++; + } + + // this means fragment list is not empty at this stage + if (closestSequenceFragIndex > -1) + { + // get the fragment with the closest sequence + TcpFragment* curTcpFrag = tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(closestSequenceFragIndex); + + // calculate number of missing bytes + uint32_t missingDataLen = curTcpFrag->sequence - tcpReassemblyData->twoSides[sideIndex].sequence; + + // update sequence + tcpReassemblyData->twoSides[sideIndex].sequence = curTcpFrag->sequence + curTcpFrag->dataLength; + if (curTcpFrag->data != nullptr) + { + // send new data to callback + if (m_OnMessageReadyCallback != nullptr) + { + // prepare missing data text + std::string missingDataTextStr = prepareMissingDataMessage(missingDataLen); + + // add missing data text to the data that will be sent to the callback. This means that the data will look something like: + // "[xx bytes missing]" + std::vector dataWithMissingDataText; + dataWithMissingDataText.reserve(missingDataTextStr.length() + curTcpFrag->dataLength); + dataWithMissingDataText.insert(dataWithMissingDataText.end(), missingDataTextStr.begin(), missingDataTextStr.end()); + dataWithMissingDataText.insert(dataWithMissingDataText.end(), curTcpFrag->data, curTcpFrag->data + curTcpFrag->dataLength); + + //TcpStreamData streamData(curTcpFrag->data, curTcpFrag->dataLength, tcpReassemblyData->connData); + TcpStreamData streamData(&dataWithMissingDataText[0], dataWithMissingDataText.size(), missingDataLen, tcpReassemblyData->connData, curTcpFrag->timestamp); + m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie); + + PCPP_LOG_DEBUG("Found missing data on side " << sideIndex << ": " << missingDataLen << " byte are missing. Sending the closest fragment which is in size " << curTcpFrag->dataLength << " + missing text message which size is " << missingDataTextStr.length()); + } + } + + // remove fragment from list + tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase(tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + closestSequenceFragIndex); + + PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments again from the start"); + + // call the method again from the start to do the whole search again (both iterations). + // the stop condition is when the list is empty (so closestSequenceFragIndex == -1) + foundSomething = true; + } + + } while (foundSomething); +} + +void TcpReassembly::closeConnection(uint32_t flowKey) +{ + closeConnectionInternal(flowKey, TcpReassembly::TcpReassemblyConnectionClosedManually); +} + +void TcpReassembly::closeConnectionInternal(uint32_t flowKey, ConnectionEndReason reason) +{ + ConnectionList::iterator iter = m_ConnectionList.find(flowKey); + if (iter == m_ConnectionList.end()) + { + PCPP_LOG_ERROR("Cannot close flow with key 0x" << std::uppercase << std::hex << flowKey << ": cannot find flow"); + return; + } + + TcpReassemblyData& tcpReassemblyData = iter->second; + + if (tcpReassemblyData.closed) // the connection is already closed + return; + + PCPP_LOG_DEBUG("Closing connection with flow key 0x" << std::hex << flowKey); + + PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 0"); + checkOutOfOrderFragments(&tcpReassemblyData, 0, true); + + PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 1"); + checkOutOfOrderFragments(&tcpReassemblyData, 1, true); + + if (m_OnConnEnd != nullptr) + m_OnConnEnd(tcpReassemblyData.connData, reason, m_UserCookie); + + tcpReassemblyData.closed = true; // mark the connection as closed + insertIntoCleanupList(flowKey); + + PCPP_LOG_DEBUG("Connection with flow key 0x" << std::hex << flowKey << " is closed"); +} + +void TcpReassembly::closeAllConnections() +{ + PCPP_LOG_DEBUG("Closing all flows"); + + ConnectionList::iterator iter = m_ConnectionList.begin(), iterEnd = m_ConnectionList.end(); + for (; iter != iterEnd; ++iter) + { + TcpReassemblyData& tcpReassemblyData = iter->second; + + if (tcpReassemblyData.closed) // the connection is already closed, skip it + continue; + + uint32_t flowKey = tcpReassemblyData.connData.flowKey; + PCPP_LOG_DEBUG("Closing connection with flow key 0x" << std::hex << flowKey); + + PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 0"); + checkOutOfOrderFragments(&tcpReassemblyData, 0, true); + + PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments on side 1"); + checkOutOfOrderFragments(&tcpReassemblyData, 1, true); + + if (m_OnConnEnd != nullptr) + m_OnConnEnd(tcpReassemblyData.connData, TcpReassemblyConnectionClosedManually, m_UserCookie); + + tcpReassemblyData.closed = true; // mark the connection as closed + insertIntoCleanupList(flowKey); + + PCPP_LOG_DEBUG("Connection with flow key 0x" << std::hex << flowKey << " is closed"); + } +} + +int TcpReassembly::isConnectionOpen(const ConnectionData& connection) const +{ + ConnectionList::const_iterator iter = m_ConnectionList.find(connection.flowKey); + if (iter != m_ConnectionList.end()) + return iter->second.closed == false; + + return -1; +} + +void TcpReassembly::insertIntoCleanupList(uint32_t flowKey) +{ + // m_CleanupList is a map with key of type time_t (expiration time). The mapped type is a list that stores the flow keys to be cleared in certain point of time. + // m_CleanupList.insert inserts an empty list if the container does not already contain an element with an equivalent key, + // otherwise this method returns an iterator to the element that prevents insertion. + std::pair pair = m_CleanupList.insert(std::make_pair(time(nullptr) + m_ClosedConnectionDelay, CleanupList::mapped_type())); + + // getting the reference to list + CleanupList::mapped_type& keysList = pair.first->second; + keysList.push_front(flowKey); +} + +uint32_t TcpReassembly::purgeClosedConnections(uint32_t maxNumToClean) +{ + uint32_t count = 0; + + if (maxNumToClean == 0) + maxNumToClean = m_MaxNumToClean; + + CleanupList::iterator iterTime = m_CleanupList.begin(), iterTimeEnd = m_CleanupList.upper_bound(time(nullptr)); + while (iterTime != iterTimeEnd && count < maxNumToClean) + { + CleanupList::mapped_type& keysList = iterTime->second; + + for (; !keysList.empty() && count < maxNumToClean; ++count) + { + CleanupList::mapped_type::const_reference key = keysList.front(); + m_ConnectionInfo.erase(key); + m_ConnectionList.erase(key); + keysList.pop_front(); + } + + if (keysList.empty()) + m_CleanupList.erase(iterTime++); + else + ++iterTime; + } + + return count; +} + +} diff --git a/Packet++/src/UdpLayer.cpp b/Packet++/src/UdpLayer.cpp index fd869445d8..f7a4906627 100644 --- a/Packet++/src/UdpLayer.cpp +++ b/Packet++/src/UdpLayer.cpp @@ -1,163 +1,163 @@ -#define LOG_MODULE PacketLogModuleUdpLayer - -#include "EndianPortable.h" -#include "UdpLayer.h" -#include "PayloadLayer.h" -#include "IPv4Layer.h" -#include "IPv6Layer.h" -#include "DnsLayer.h" -#include "DhcpLayer.h" -#include "DhcpV6Layer.h" -#include "VxlanLayer.h" -#include "SipLayer.h" -#include "RadiusLayer.h" -#include "GtpLayer.h" -#include "NtpLayer.h" -#include "SomeIpLayer.h" -#include "WakeOnLanLayer.h" -#include "PacketUtils.h" -#include "Logger.h" -#include -#include - -namespace pcpp -{ - -UdpLayer::UdpLayer(uint16_t portSrc, uint16_t portDst) -{ - const size_t headerLen = sizeof(udphdr); - m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, headerLen); - udphdr* udpHdr = (udphdr*)m_Data; - udpHdr->portDst = htobe16(portDst); - udpHdr->portSrc = htobe16(portSrc); - m_Protocol = UDP; -} - -uint16_t UdpLayer::getSrcPort() const -{ - return be16toh(getUdpHeader()->portSrc); -} - -uint16_t UdpLayer::getDstPort() const -{ - return be16toh(getUdpHeader()->portDst); -} - -uint16_t UdpLayer::calculateChecksum(bool writeResultToPacket) -{ - udphdr* udpHdr = (udphdr*)m_Data; - uint16_t checksumRes = 0; - uint16_t currChecksumValue = udpHdr->headerChecksum; - - if (m_PrevLayer != nullptr) - { - udpHdr->headerChecksum = 0; - ScalarBuffer vec[2]; - PCPP_LOG_DEBUG("data len = " << m_DataLen); - vec[0].buffer = (uint16_t*)m_Data; - vec[0].len = m_DataLen; - - if (m_PrevLayer->getProtocol() == IPv4) - { - uint32_t srcIP = ((IPv4Layer*)m_PrevLayer)->getSrcIPv4Address().toInt(); - uint32_t dstIP = ((IPv4Layer*)m_PrevLayer)->getDstIPv4Address().toInt(); - uint16_t pseudoHeader[6]; - pseudoHeader[0] = srcIP >> 16; - pseudoHeader[1] = srcIP & 0xFFFF; - pseudoHeader[2] = dstIP >> 16; - pseudoHeader[3] = dstIP & 0xFFFF; - pseudoHeader[4] = 0xffff & udpHdr->length; - pseudoHeader[5] = htobe16(0x00ff & PACKETPP_IPPROTO_UDP); - vec[1].buffer = pseudoHeader; - vec[1].len = 12; - checksumRes = computeChecksum(vec, 2); - PCPP_LOG_DEBUG("calculated checksum = 0x" << std::uppercase << std::hex << checksumRes); - } - else if (m_PrevLayer->getProtocol() == IPv6) - { - uint16_t pseudoHeader[18]; - ((IPv6Layer*)m_PrevLayer)->getSrcIPv6Address().copyTo((uint8_t*)pseudoHeader); - ((IPv6Layer*)m_PrevLayer)->getDstIPv6Address().copyTo((uint8_t*)(pseudoHeader+8)); - pseudoHeader[16] = 0xffff & udpHdr->length; - pseudoHeader[17] = htobe16(0x00ff & PACKETPP_IPPROTO_UDP); - vec[1].buffer = pseudoHeader; - vec[1].len = 36; - checksumRes = computeChecksum(vec, 2); - PCPP_LOG_DEBUG("calculated checksum = 0x" << std::uppercase << std::hex << checksumRes); - } - } - - if (checksumRes == 0) - checksumRes = 0xffff; - - if(writeResultToPacket) - udpHdr->headerChecksum = htobe16(checksumRes); - else - udpHdr->headerChecksum = currChecksumValue; - - return checksumRes; -} - -void UdpLayer::parseNextLayer() -{ - if (m_DataLen <= sizeof(udphdr)) - return; - - uint16_t portDst = getDstPort(); - uint16_t portSrc = getSrcPort(); - - uint8_t* udpData = m_Data + sizeof(udphdr); - size_t udpDataLen = m_DataLen - sizeof(udphdr); - - if ((portSrc == 68 && portDst == 67) || (portSrc == 67 && portDst == 68) || (portSrc == 67 && portDst == 67)) - 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 ((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 (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 - m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); -} - -void UdpLayer::computeCalculateFields() -{ - udphdr* udpHdr = (udphdr*)m_Data; - udpHdr->length = htobe16(m_DataLen); - calculateChecksum(true); -} - -std::string UdpLayer::toString() const -{ - std::ostringstream srcPortStream; - srcPortStream << getSrcPort(); - std::ostringstream dstPortStream; - dstPortStream << getDstPort(); - - return "UDP Layer, Src port: " + srcPortStream.str() + ", Dst port: " + dstPortStream.str(); -} - -} // namespace pcpp +#define LOG_MODULE PacketLogModuleUdpLayer + +#include "EndianPortable.h" +#include "UdpLayer.h" +#include "PayloadLayer.h" +#include "IPv4Layer.h" +#include "IPv6Layer.h" +#include "DnsLayer.h" +#include "DhcpLayer.h" +#include "DhcpV6Layer.h" +#include "VxlanLayer.h" +#include "SipLayer.h" +#include "RadiusLayer.h" +#include "GtpLayer.h" +#include "NtpLayer.h" +#include "SomeIpLayer.h" +#include "WakeOnLanLayer.h" +#include "PacketUtils.h" +#include "Logger.h" +#include +#include + +namespace pcpp +{ + +UdpLayer::UdpLayer(uint16_t portSrc, uint16_t portDst) +{ + const size_t headerLen = sizeof(udphdr); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + udphdr* udpHdr = (udphdr*)m_Data; + udpHdr->portDst = htobe16(portDst); + udpHdr->portSrc = htobe16(portSrc); + m_Protocol = UDP; +} + +uint16_t UdpLayer::getSrcPort() const +{ + return be16toh(getUdpHeader()->portSrc); +} + +uint16_t UdpLayer::getDstPort() const +{ + return be16toh(getUdpHeader()->portDst); +} + +uint16_t UdpLayer::calculateChecksum(bool writeResultToPacket) +{ + udphdr* udpHdr = (udphdr*)m_Data; + uint16_t checksumRes = 0; + uint16_t currChecksumValue = udpHdr->headerChecksum; + + if (m_PrevLayer != nullptr) + { + udpHdr->headerChecksum = 0; + ScalarBuffer vec[2]; + PCPP_LOG_DEBUG("data len = " << m_DataLen); + vec[0].buffer = (uint16_t*)m_Data; + vec[0].len = m_DataLen; + + if (m_PrevLayer->getProtocol() == IPv4) + { + uint32_t srcIP = ((IPv4Layer*)m_PrevLayer)->getSrcIPv4Address().toInt(); + uint32_t dstIP = ((IPv4Layer*)m_PrevLayer)->getDstIPv4Address().toInt(); + uint16_t pseudoHeader[6]; + pseudoHeader[0] = srcIP >> 16; + pseudoHeader[1] = srcIP & 0xFFFF; + pseudoHeader[2] = dstIP >> 16; + pseudoHeader[3] = dstIP & 0xFFFF; + pseudoHeader[4] = 0xffff & udpHdr->length; + pseudoHeader[5] = htobe16(0x00ff & PACKETPP_IPPROTO_UDP); + vec[1].buffer = pseudoHeader; + vec[1].len = 12; + checksumRes = computeChecksum(vec, 2); + PCPP_LOG_DEBUG("calculated checksum = 0x" << std::uppercase << std::hex << checksumRes); + } + else if (m_PrevLayer->getProtocol() == IPv6) + { + uint16_t pseudoHeader[18]; + ((IPv6Layer*)m_PrevLayer)->getSrcIPv6Address().copyTo((uint8_t*)pseudoHeader); + ((IPv6Layer*)m_PrevLayer)->getDstIPv6Address().copyTo((uint8_t*)(pseudoHeader+8)); + pseudoHeader[16] = 0xffff & udpHdr->length; + pseudoHeader[17] = htobe16(0x00ff & PACKETPP_IPPROTO_UDP); + vec[1].buffer = pseudoHeader; + vec[1].len = 36; + checksumRes = computeChecksum(vec, 2); + PCPP_LOG_DEBUG("calculated checksum = 0x" << std::uppercase << std::hex << checksumRes); + } + } + + if (checksumRes == 0) + checksumRes = 0xffff; + + if(writeResultToPacket) + udpHdr->headerChecksum = htobe16(checksumRes); + else + udpHdr->headerChecksum = currChecksumValue; + + return checksumRes; +} + +void UdpLayer::parseNextLayer() +{ + if (m_DataLen <= sizeof(udphdr)) + return; + + uint16_t portDst = getDstPort(); + uint16_t portSrc = getSrcPort(); + + uint8_t* udpData = m_Data + sizeof(udphdr); + size_t udpDataLen = m_DataLen - sizeof(udphdr); + + if ((portSrc == 68 && portDst == 67) || (portSrc == 67 && portDst == 68) || (portSrc == 67 && portDst == 67)) + 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 ((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 (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 + m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); +} + +void UdpLayer::computeCalculateFields() +{ + udphdr* udpHdr = (udphdr*)m_Data; + udpHdr->length = htobe16(m_DataLen); + calculateChecksum(true); +} + +std::string UdpLayer::toString() const +{ + std::ostringstream srcPortStream; + srcPortStream << getSrcPort(); + std::ostringstream dstPortStream; + dstPortStream << getDstPort(); + + return "UDP Layer, Src port: " + srcPortStream.str() + ", Dst port: " + dstPortStream.str(); +} + +} // namespace pcpp diff --git a/Pcap++/header/DpdkDevice.h b/Pcap++/header/DpdkDevice.h index d26c92de0e..3902116158 100644 --- a/Pcap++/header/DpdkDevice.h +++ b/Pcap++/header/DpdkDevice.h @@ -1,829 +1,829 @@ -#ifndef PCAPPP_DPDK_DEVICE -#define PCAPPP_DPDK_DEVICE - -#include -#include -#include -#include "MacAddress.h" -#include "SystemUtils.h" -#include "Device.h" -#include "MBufRawPacket.h" - -/** - * @file - * This file and DpdkDeviceList.h provide PcapPlusPlus C++ wrapper for DPDK (stands for data-plan development kit). What is - * DPDK? as quoting from http://dpdk.org: "DPDK is a set of libraries and drivers for fast packet processing... These libraries can be used to: - * receive and send packets within the minimum number of CPU cycles (usually less than 80 cycles)... develop fast packet capture algorithms - * (tcpdump-like)... run third-party fast path stacks... Some packet processing functions have been benchmarked up to hundreds million - * frames per second, using 64-byte packets with a PCIe NIC"
- * As DPDK API is written in C, PcapPlusPlus wraps the main functionality in a C++ easy-to-use classes which should have minimum affect - * on performance and packet processing rate. In addition it brings DPDK to the PcapPlusPlus framework and API so you can use DPDK - * together with other PcapPlusPlus features such as packet parsing and editing, etc.
- * So how DPDK basically works? in order to boost packet processing performance on a commodity server DPDK is bypassing the Linux kernel. - * All the packet processing activity happens in the user space so basically packets are delivered from NIC hardware queues directly - * to user-space shared memory without going through the kernel. In addition DPDK uses polling instead of handling interrupts for each - * arrived packet (as interrupts create some delays). Other methods to boost packets processing implemented by DPDK are using Hugepages to - * decrease the size of TLB that results in a much faster virtual to physical page conversion, thread affinity to bind threads to a - * specific core, lock-free user-space multi-core synchronization using rings data structures and NUMA awareness to avoid expensive data - * transfers between sockets.
- * Not every NIC supports kernel-bypass capabilities so DPDK cannot work with any NIC. The list of supported NICs are in DPDK's web-site - * http://dpdk.org/doc/nics . For each such NIC the DPDK framework provides a module that called poll-mode-driver (PMD in short) that - * enables this NIC to the working with DPDK. PcapPlusPlus wasn't tested with most PMDs but all of them should theoretically work as - * PcapPlusPlus doesn't change the PMD behavior
- * DPDK has another basic data-structure called mbuf. An mbuf is DPDK wrapper struct for network packets. When working with packets - * in DPDK you actually work with mbufs. The mbuf contains the packet data (obviously) but also some metadata on the packet such - * as the DPDK port it was captured on, packet ref-count (which allows it to be referenced by several objects), etc. One important - * concept is that DPDK doesn't allocate mbufs on-the-fly but uses mbuf pools. These pools is allocated on application startup and - * used throughout the application. The goal of this, of course, is increasing packet processing performance as allocating memory has - * its cost. So pool size is important and varies between applications. For example: an application that stores packets in memory - * has to have a large pool of mbufs so mbufs doesn't run-out. PcapPlusPlus enables to choose the pool size at startup
- *
- * PcapPlusPlus main wrapper classes for DPDK are: - * - DpdkDevice - a class that wraps a DPDK port and provides all capabilities of receiving and sending packets to this port - * - DpdkDeviceList - a singleton class that initializes the DPDK infrastructure and creates DpdkDevice instances to all available ports. - * In addition it allows starting and stopping of worker threads - * - MBufRawPacket - a child class to RawPacket which customizes it for working with mbuf - * - In addition PcapPlusPlus provides a shell script to initialize DPDK prerequisites: setup_dpdk.py. This is an easy-to-use script - * that sets up huge-pages, loads DPDK kernel module and sets up the NICs that will be used by DPDK. This script must run before an - * application that uses DPDK runs. If you forgot to run it the application will fail with an appropriate error that will remind - * - * DPDK initialization using PcapPlusPlus: - * - Before application runs: run the setup_dpdk.py script - * - On application startup call DpdkDeviceList#initDpdk() static method to initialize DPDK infrastructure and DpdkDevice instances - * - Open the relevant DpdkDevice(s) - * - Send & receive packets... - */ - -struct rte_mbuf; -struct rte_mempool; -struct rte_eth_conf; -struct rte_eth_dev_tx_buffer; - -/** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - -#define DPDK_MAX_RX_QUEUES 16 -#define DPDK_MAX_TX_QUEUES 16 -#define PCPP_RSS_HASH_MAGIC_NUMBER 0x123456 - - class DpdkDeviceList; - class DpdkDevice; - - /** - * An enum describing all PMD (poll mode driver) types supported by DPDK. For more info about these PMDs please visit the DPDK web-site - */ - enum DpdkPMDType - { - /** Unknown PMD type */ - PMD_UNKNOWN, - /** Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple (slave) NICs into a single logical interface*/ - PMD_BOND, - /** Intel E1000 PMD */ - PMD_E1000EM, - /** Intel 1GbE PMD */ - PMD_IGB, - /** Intel 1GbE virtual function PMD */ - PMD_IGBVF, - /** Cisco enic (UCS Virtual Interface Card) PMD */ - PMD_ENIC, - /** Intel fm10k PMD */ - PMD_FM10K, - /** Intel 40GbE PMD */ - PMD_I40E, - /** Intel 40GbE virtual function PMD */ - PMD_I40EVF, - /** Intel 10GbE PMD */ - PMD_IXGBE, - /** Intel 10GbE virtual function PMD */ - PMD_IXGBEVF, - /** Mellanox ConnectX-3, ConnectX-3 Pro PMD */ - PMD_MLX4, - /** Null PMD */ - PMD_NULL, - /** pcap file PMD */ - PMD_PCAP, - /** ring-based (memory) PMD */ - PMD_RING, - /** VirtIO PMD */ - PMD_VIRTIO, - /** VMWare VMXNET3 PMD */ - PMD_VMXNET3, - /** Xen Project PMD */ - PMD_XENVIRT, - /** AF_PACKET PMD */ - PMD_AF_PACKET - }; - - /** - * @typedef OnDpdkPacketsArriveCallback - * A callback that is called when a burst of packets are captured by DpdkDevice - * @param[in] packets A pointer to an array of MBufRawPacket - * @param[in] numOfPackets The length of the array - * @param[in] threadId The thread/core ID who captured the packets - * @param[in] device A pointer to the DpdkDevice who captured the packets - * @param[in] userCookie The user cookie assigned by the user in DpdkDevice#startCaptureSingleThread() or DpdkDevice#startCaptureMultiThreads - */ - typedef void (*OnDpdkPacketsArriveCallback)(MBufRawPacket* packets, uint32_t numOfPackets, uint8_t threadId, DpdkDevice* device, void* userCookie); - - /** - * @class DpdkDevice - * Encapsulates a DPDK port and enables receiving and sending packets using DPDK as well as getting interface info & status, packet - * statistics, etc. This class has no public c'tor as it's constructed by DpdkDeviceList during initialization.
- * - * __RX/TX queues__: modern NICs provide hardware load-balancing for packets. This means that each packet received by the NIC is hashed - * by one or more parameter (IP address, port, etc.) and goes into one of several RX queues provided by the NIC. This enables - * applications to work in a multi-core environment where each core can read packets from different RX queue(s). Same goes for TX - * queues: it's possible to write packets to different TX queues and the NIC is taking care of sending them to the network. - * Different NICs provide different number of RX and TX queues. DPDK supports this capability and enables the user to open the - * DPDK port (DpdkDevice) with a single or multiple RX and TX queues. When receiving packets the user can decide from which RX queue - * to read from, and when transmitting packets the user can decide to which TX queue to send them to. RX/TX queues are configured - * when opening the DpdkDevice (see openMultiQueues())
- * - * __Capturing packets__: there are two ways to capture packets using DpdkDevice: - * - using worker threads (see DpdkDeviceList#startDpdkWorkerThreads() ). When using this method the worker should use the - * DpdkDevice#receivePackets() methods to get packets from the DpdkDevice - * - by setting a callback which is invoked each time a burst of packets arrives. For more details see - * DpdkDevice#startCaptureSingleThread() - * - * __Sending packets:__ DpdkDevice has various methods for sending packets. They enable sending raw packets, parsed packets, etc. - * for all opened TX queues. Also, DPDK provides an option to buffer TX packets and send them only when reaching a certain threshold (you - * can read more about it here: http://dpdk.org/doc/api/rte__ethdev_8h.html#a0e941a74ae1b1b886764bc282458d946). DpdkDevice supports that - * option as well. See DpdkDevice#sendPackets()
- * - * __Get interface info__: DpdkDevice provides all kind of information on the interface/device such as MAC address, MTU, link status, - * PCI address, PMD (poll-mode-driver) used for this port, etc. In addition it provides RX/TX statistics when receiving or sending - * packets
- * - * __Known limitations:__ - * - BPF filters are currently not supported by this device (as opposed to other PcapPlusPlus device types. This means that the - * device cannot filter packets before they get to the user - * - It's not possible to set or change NIC load-balancing method. DPDK provides this capability but it's still not - * supported by DpdkDevice - */ - class DpdkDevice : public IDevice - { - friend class DpdkDeviceList; - friend class MBufRawPacket; - public: - - /** - * An enum describing all RSS (Receive Side Scaling) hash functions supported in DPDK. Notice not all - * PMDs support all types of hash functions - */ - enum DpdkRssHashFunction - { - /** No RSS */ - RSS_NONE = 0, - /** IPv4 based flow */ - RSS_IPV4 = 0x1, - /** Fragmented IPv4 based flow */ - RSS_FRAG_IPV4 = 0x2, - /** Non-fragmented IPv4 + TCP flow */ - RSS_NONFRAG_IPV4_TCP = 0x4, - /** Non-fragmented IPv4 + UDP flow */ - RSS_NONFRAG_IPV4_UDP = 0x8, - /** Non-fragmented IPv4 + SCTP flow */ - RSS_NONFRAG_IPV4_SCTP = 0x10, - /** Non-fragmented IPv4 + non TCP/UDP/SCTP flow */ - RSS_NONFRAG_IPV4_OTHER = 0x20, - /** IPv6 based flow */ - RSS_IPV6 = 0x40, - /** Fragmented IPv6 based flow */ - RSS_FRAG_IPV6 = 0x80, - /** Non-fragmented IPv6 + TCP flow */ - RSS_NONFRAG_IPV6_TCP = 0x100, - /** Non-fragmented IPv6 + UDP flow */ - RSS_NONFRAG_IPV6_UDP = 0x200, - /** Non-fragmented IPv6 + SCTP flow */ - RSS_NONFRAG_IPV6_SCTP = 0x400, - /** Non-fragmented IPv6 + non TCP/UDP/SCTP flow */ - RSS_NONFRAG_IPV6_OTHER = 0x800, - /** L2 payload based flow */ - RSS_L2_PAYLOAD = 0x1000, - /** IPv6 Ex based flow */ - RSS_IPV6_EX = 0x2000, - /** IPv6 + TCP Ex based flow */ - RSS_IPV6_TCP_EX = 0x4000, - /** IPv6 + UDP Ex based flow */ - RSS_IPV6_UDP_EX = 0x8000, - /** Consider device port number as a flow differentiator */ - RSS_PORT = 0x10000, - /** VXLAN protocol based flow */ - RSS_VXLAN = 0x20000, - /** GENEVE protocol based flow */ - RSS_GENEVE = 0x40000, - /** NVGRE protocol based flow */ - RSS_NVGRE = 0x80000, - /** All RSS functions supported by the device */ - RSS_ALL_SUPPORTED = -1, - /** A default set of RSS functions supported by the device */ - RSS_DEFAULT = PCPP_RSS_HASH_MAGIC_NUMBER - }; - - /** - * @struct DpdkDeviceConfiguration - * A struct that contains user configurable parameters for opening a DpdkDevice. All of these parameters have default values so - * the user doesn't have to use these parameters or understand exactly what is their effect - */ - struct DpdkDeviceConfiguration - { - /** - * When configuring a DPDK RX queue, DPDK creates descriptors it will use for receiving packets from the network to this RX queue. - * This parameter enables to configure the number of descriptors that will be created for each RX queue - */ - uint16_t receiveDescriptorsNumber; - - /** - * When configuring a DPDK TX queue, DPDK creates descriptors it will use for transmitting packets to the network through this TX queue. - * This parameter enables to configure the number of descriptors that will be created for each TX queue - */ - uint16_t transmitDescriptorsNumber; - - /** - * Set the TX buffer flush timeout in millisecond (only relevant if sending packets using DPDK TX buffer mechanism). - * A value of zero means no timeout - */ - uint16_t flushTxBufferTimeout; - - /** - * When configuring a DPDK device, DPDK supports to activate the Receive Side Scaling (RSS) feature to distribute traffic between the RX queues - * This parameter points to an array holding the RSS key to use for hashing specific header fields of received packets. - * The length of this array should be indicated by rssKeyLength below. - * Supplying a NULL value causes a default random hash key to be used by the device driver - */ - uint8_t* rssKey; - - /** - * This parameter indicates the length in bytes of the array pointed by rssKey. - * This length will be checked in i40e only. Others assume 40 bytes to be used. - */ - uint8_t rssKeyLength; - - /** - * This parameter enables to configure the types of packets to which the RSS hashing must be applied. The value - * is a mask composed of hash functions described in DpdkRssHashFunction enum. Supplying a value equal to zero - * disables the RSS feature. Supplying a value equal to -1 enables all hash functions supported by this PMD - */ - uint64_t rssHashFunction; - - /** - * A c'tor for this struct - * @param[in] receiveDescriptorsNumber An optional parameter for defining the number of RX descriptors that will be allocated for each RX queue. - * Default value is 128 - * @param[in] transmitDescriptorsNumber An optional parameter for defining the number of TX descriptors that will be allocated for each TX queue. - * Default value is 512 - * @param[in] flushTxBufferTimeout An optional parameter for setting TX buffer timeout in usec. Default value is 100 usec - * @param[in] rssHashFunction This parameter enable to configure the types of packets to which the RSS hashing must be applied. - * The value provided here should be a mask composed of hash functions described in DpdkRssHashFunction enum. - * The default value is RSS_DEFAULT. - * @param[in] rssKey A pointer to an array holding the RSS key to use for hashing specific header of received packets. If not - * specified, there is a default key defined inside DpdkDevice - * @param[in] rssKeyLength The length in bytes of the array pointed by rssKey. Default value is the length of default rssKey - */ - explicit DpdkDeviceConfiguration(uint16_t receiveDescriptorsNumber = 128, - uint16_t transmitDescriptorsNumber = 512, - uint16_t flushTxBufferTimeout = 100, - uint64_t rssHashFunction = RSS_DEFAULT, - uint8_t* rssKey = DpdkDevice::m_RSSKey, - uint8_t rssKeyLength = 40) - { - this->receiveDescriptorsNumber = receiveDescriptorsNumber; - this->transmitDescriptorsNumber = transmitDescriptorsNumber; - this->flushTxBufferTimeout = flushTxBufferTimeout; - this->rssKey = rssKey; - this->rssKeyLength = rssKeyLength; - this->rssHashFunction = rssHashFunction; - } - }; - - /** - * @struct LinkStatus - * A struct that contains the link status of a DpdkDevice (DPDK port). Returned from DpdkDevice#getLinkStatus() - */ - struct LinkStatus - { - /** Enum for describing link duplex */ - enum LinkDuplex - { - /** Full duplex */ - FULL_DUPLEX, - /** Half duplex */ - HALF_DUPLEX - }; - - /** True if link is up, false if it's down */ - bool linkUp; - /** Link speed in Mbps (for example: 10Gbe will show 10000) */ - int linkSpeedMbps; - /** Link duplex (half/full duplex) */ - LinkDuplex linkDuplex; - }; - - /** - * @struct RxTxStats - * A container for RX/TX statistics - */ - struct RxTxStats - { - /** Total number of packets */ - uint64_t packets; - /** Total number of successfully received bytes */ - uint64_t bytes; - /** Packets per second */ - uint64_t packetsPerSec; - /** Bytes per second */ - uint64_t bytesPerSec; - }; - - /** - * @struct DpdkDeviceStats - * A container for DpdkDevice statistics - */ - struct DpdkDeviceStats - { - /** DpdkDevice ID */ - uint8_t devId; - /** The timestamp of when the stats were written */ - timespec timestamp; - /** RX statistics per RX queue */ - RxTxStats rxStats[DPDK_MAX_RX_QUEUES]; - /** TX statistics per TX queue */ - RxTxStats txStats[DPDK_MAX_RX_QUEUES]; - /** RX statistics, aggregated for all RX queues */ - RxTxStats aggregatedRxStats; - /** TX statistics, aggregated for all TX queues */ - RxTxStats aggregatedTxStats; - /** Total number of RX packets dropped by H/W because there are no available buffers (i.e RX queues are full) */ - uint64_t rxPacketsDroppedByHW; - /** Total number of erroneous packets */ - uint64_t rxErroneousPackets; - /** Total number of RX mbuf allocation failuers */ - uint64_t rxMbufAlocFailed; - }; - - virtual ~DpdkDevice(); - - /** - * @return The device ID (DPDK port ID) - */ - int getDeviceId() const { return m_Id; } - /** - * @return The device name which is in the format of 'DPDK_[PORT-ID]' - */ - std::string getDeviceName() const { return m_DeviceName; } - - /** - * @return The MAC address of the device (DPDK port) - */ - MacAddress getMacAddress() const { return m_MacAddress; } - - /** - * @return The name of the PMD (poll mode driver) DPDK is using for this device. You can read about PMDs in the DPDK documentation: - * http://dpdk.org/doc/guides/prog_guide/poll_mode_drv.html - */ - std::string getPMDName() const { return m_PMDName; } - - /** - * @return The enum type of the PMD (poll mode driver) DPDK is using for this device. You can read about PMDs in the DPDK documentation: - * http://dpdk.org/doc/guides/prog_guide/poll_mode_drv.html - */ - DpdkPMDType getPMDType() const { return m_PMDType; } - - /** - * @return The PCI address of the device - */ - std::string getPciAddress() const { return m_PciAddress; } - - /** - * @return The device's maximum transmission unit (MTU) in bytes - */ - uint16_t getMtu() const { return m_DeviceMtu; } - - /** - * Set a new maximum transmission unit (MTU) for this device - * @param[in] newMtu The new MTU in bytes - * @return True if MTU was set successfully, false if operation failed or if PMD doesn't support changing the MTU - */ - bool setMtu(uint16_t newMtu); - - /** - * @return True if this device is a virtual interface (such as VMXNET3, 1G/10G virtual function, etc.), false otherwise - */ - bool isVirtual() const; - - /** - * Get the link status (link up/down, link speed and link duplex) - * @param[out] linkStatus A reference to object the result shall be written to - */ - void getLinkStatus(LinkStatus& linkStatus) const; - - /** - * @return The core ID used in this context - */ - uint32_t getCurrentCoreId() const; - - /** - * @return The number of RX queues currently opened for this device (as configured in openMultiQueues() ) - */ - uint16_t getNumOfOpenedRxQueues() const { return m_NumOfRxQueuesOpened; } - - /** - * @return The number of TX queues currently opened for this device (as configured in openMultiQueues() ) - */ - uint16_t getNumOfOpenedTxQueues() const { return m_NumOfTxQueuesOpened; } - - /** - * @return The total number of RX queues available on this device - */ - uint16_t getTotalNumOfRxQueues() const { return m_TotalAvailableRxQueues; } - - /** - * @return The total number of TX queues available on this device - */ - uint16_t getTotalNumOfTxQueues() const { return m_TotalAvailableTxQueues; } - - - /** - * Receive raw packets from the network - * @param[out] rawPacketsArr A vector where all received packets will be written into - * @param[in] rxQueueId The RX queue to receive packets from - * @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log - */ - uint16_t receivePackets(MBufRawPacketVector& rawPacketsArr, uint16_t rxQueueId) const; - - /** - * Receive raw packets from the network. Please notice that in terms of performance, this is the best method to use - * for receiving packets because out of all receivePackets overloads this method requires the least overhead and is - * almost as efficient as receiving packets directly through DPDK. So if performance is a critical factor in your - * application, please use this method - * @param[out] rawPacketsArr A pointer to an array of MBufRawPacket pointers where all received packets will be written into. The array is expected to - * be allocated by the user and its length should be provided in rawPacketArrLength. Number of packets received will be returned. - * Notice it's the user responsibility to free the array and its content when done using it - * @param[out] rawPacketArrLength The length of MBufRawPacket pointers array - * @param[in] rxQueueId The RX queue to receive packets from - * @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log - */ - uint16_t receivePackets(MBufRawPacket** rawPacketsArr, uint16_t rawPacketArrLength, uint16_t rxQueueId) const; - - /** - * Receive parsed packets from the network - * @param[out] packetsArr A pointer to an allocated array of Packet pointers where all received packets will be written into. The array is expected to - * be allocated by the user and its length should be provided in packetsArrLength. Number of packets received will be returned. - * Notice it's the user responsibility to free the array and its content when done using it - * @param[out] packetsArrLength The length of Packet pointers array - * @param[in] rxQueueId The RX queue to receive packets from - * @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log - */ - uint16_t receivePackets(Packet** packetsArr, uint16_t packetsArrLength, uint16_t rxQueueId) const; - - /** - * Send an array of MBufRawPacket to the network. Please notice the following:
- * - In terms of performance, this is the best method to use for sending packets because out of all sendPackets overloads - * this method requires the least overhead and is almost as efficient as sending the packets directly through DPDK. So if performance - * is a critical factor in your application, please use this method - * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each - * iteration of 64 packets - * - If the number of packets to send is higher than a threshold of 80% of total TX descriptors (which is typically around 400 packets), - * then after reaching this threshold there is a built-in 0.2 sec sleep to let the TX descriptors clean - * - The mbufs used in this method aren't freed by this method, they will be transparently freed by DPDK - *

- * @param[in] rawPacketsArr A pointer to an array of MBufRawPacket - * @param[in] arrLength The length of the array - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packets will be sent to. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return The number of packets actually and successfully sent. If device is not opened or TX queue isn't open, 0 will be returned. - * Also, if TX buffer is being used and packets are buffered, some or all may not be actually sent - */ - uint16_t sendPackets(MBufRawPacket** rawPacketsArr, uint16_t arrLength, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Send an array of parsed packets to the network. Please notice the following:
- * - If some or all of the packets contain raw packets which aren't of type MBufRawPacket, a new temp MBufRawPacket instances - * will be created and packet data will be copied to them. This is necessary to allocate mbufs which will store the data to be sent. - * If performance is a critical factor please make sure you send parsed packets that contain only raw packets of type MBufRawPacket - * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each - * iteration of 64 packets - * - If the number of packets to send is higher than a threshold of 80% of total TX descriptors (which is typically around 400 packets), - * then after reaching this threshold there is a built-in 0.2 sec sleep to let the TX descriptors clean - * - The mbufs used or allocated in this method aren't freed by this method, they will be transparently freed by DPDK - *

- * @param[in] packetsArr A pointer to an array of parsed packet pointers - * @param[in] arrLength The length of the array - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packets will be sent to. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return The number of packets actually and successfully sent. If device is not opened or TX queue isn't open, 0 will be returned. - * Also, if TX buffer is being used and packets are buffered, some or all may not be actually sent - */ - uint16_t sendPackets(Packet** packetsArr, uint16_t arrLength, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Send a vector of MBufRawPacket pointers to the network. Please notice the following:
- * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each - * iteration of 64 packets - * - If the number of packets to send is higher than a threshold of 80% of total TX descriptors (which is typically around 400 packets), - * then after reaching this threshold there is a built-in 0.2 sec sleep to let the TX descriptors clean - * - The mbufs used in this method aren't freed by this method, they will be transparently freed by DPDK - *

- * @param[in] rawPacketsVec The vector of raw packet - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packets will be sent to. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return The number of packets actually and successfully sent. If device is not opened or TX queue isn't open, 0 will be returned. - * Also, if TX buffer is being used and packets are buffered, some or all may not be actually sent - */ - uint16_t sendPackets(MBufRawPacketVector& rawPacketsVec, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Send a vector of RawPacket pointers to the network. Please notice the following:
- * - If some or all of the raw packets aren't of type MBufRawPacket, a new temp MBufRawPacket instances will be created - * and packet data will be copied to them. This is necessary to allocate mbufs which will store the data to be sent. If - * performance is a critical factor please make sure you send only raw packets of type MBufRawPacket (or use the sendPackets overload - * that sends MBufRawPacketVector) - * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each - * iteration of 64 packets - * - If the number of packets to send is higher than a threshold of 80% of total TX descriptors (which is typically around 400 packets), - * then after reaching this threshold there is a built-in 0.2 sec sleep to let the TX descriptors clean - * - The mbufs used or allocated in this method aren't freed by this method, they will be transparently freed by DPDK - *

- * @param[in] rawPacketsVec The vector of raw packet - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packets will be sent to. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return The number of packets actually and successfully sent. If device is not opened or TX queue isn't open, 0 will be returned. - * Also, if TX buffer is being used and packets are buffered, some or all may not be actually sent - */ - uint16_t sendPackets(RawPacketVector& rawPacketsVec, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Send a raw packet to the network. Please notice that if the raw packet isn't of type MBufRawPacket, a new temp MBufRawPacket - * will be created and the data will be copied to it. This is necessary to allocate an mbuf which will store the data to be sent. - * If performance is a critical factor please make sure you send a raw packet of type MBufRawPacket. Please also notice that the - * mbuf used or allocated in this method isn't freed by this method, it will be transparently freed by DPDK - * @param[in] rawPacket The raw packet to send - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packet will be sent to. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return True if packet was sent successfully or false if device is not opened, TX queue isn't opened, or if the packet wasn't - * sent for any other reason. Please notice that when using TX buffers the packet may be buffered and not sent immediately, which - * may also result in returning false - */ - bool sendPacket(RawPacket& rawPacket, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Send a MBufRawPacket to the network. Please notice that the mbuf used in this method isn't freed by this method, it will be - * transparently freed by DPDK - * @param[in] rawPacket The MBufRawPacket to send - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packet will be sent to. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return True if packet was sent successfully or false if device is not opened, TX queue isn't opened, or if the packet wasn't - * sent for any other reason. Please notice that when using TX buffers the packet may be buffered and not sent immediately, which - * may also result in returning false - */ - bool sendPacket(MBufRawPacket& rawPacket, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Send a parsed packet to the network. Please notice that the mbuf used or allocated in this method isn't freed by this method, - * it will be transparently freed by DPDK - * @param[in] packet The parsed packet to send. Please notice that if the packet contains a raw packet which isn't of type - * MBufRawPacket, a new temp MBufRawPacket will be created and the data will be copied to it. This is necessary to - * allocate an mbuf which will store the data to be sent. If performance is a critical factor please make sure you send a - * parsed packet that contains a raw packet of type MBufRawPacket - * @param[in] txQueueId An optional parameter which indicates to which TX queue the packet will be sent on. The default is - * TX queue 0 - * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's - * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) - * @return True if packet was sent successfully or false if device is not opened, TX queue isn't opened, or if the packet wasn't - * sent for any other reason. Please notice that when using TX buffers the packet may be buffered and not sent immediately, which - * may also result in returning false - */ - bool sendPacket(Packet& packet, uint16_t txQueueId = 0, bool useTxBuffer = false); - - /** - * Overridden method from IPcapDevice. __BPF filters are currently not implemented for DpdkDevice__ - * @param[in] filter Not used in this method - * @return Always false with a "Filters aren't supported in DPDK device" error message - */ - bool setFilter(GeneralFilter& filter); - - /** - * Overridden method from IPcapDevice. __BPF filters are currently not implemented for DpdkDevice__ - * @param[in] filterAsString Not used in this method - * @return Always false with a "Filters aren't supported in DPDK device" error message - */ - bool setFilter(std::string filterAsString); - - /** - * Open the DPDK device. Notice opening the device only makes it ready to use, it doesn't start packet capturing. This method initializes RX and TX queues, - * configures the DPDK port and starts it. Call close() to close the device. The device is opened in promiscuous mode - * @param[in] numOfRxQueuesToOpen Number of RX queues to setup. This number must be smaller or equal to the return value of getTotalNumOfRxQueues() - * @param[in] numOfTxQueuesToOpen Number of TX queues to setup. This number must be smaller or equal to the return value of getTotalNumOfTxQueues() - * @param[in] config Optional parameter for defining special port configuration parameters such as number of receive/transmit descriptors. If not set the default - * parameters will be set (see DpdkDeviceConfiguration) - * @return True if the device was opened successfully, false if device is already opened, if RX/TX queues configuration failed or of DPDK port - * configuration and startup failed - */ - bool openMultiQueues(uint16_t numOfRxQueuesToOpen, uint16_t numOfTxQueuesToOpen, const DpdkDeviceConfiguration& config = DpdkDeviceConfiguration()); - - /** - * There are two ways to capture packets using DpdkDevice: one of them is using worker threads (see DpdkDeviceList#startDpdkWorkerThreads() ) and - * the other way is setting a callback which is invoked each time a burst of packets is captured. This method implements the second way. - * After invoking this method the DpdkDevice enters capture mode and starts capturing packets. - * This method assumes there is only 1 RX queue opened for this device, otherwise an error is returned. It then allocates a core and creates 1 thread - * that runs in an endless loop and tries to capture packets using DPDK. Each time a burst of packets is captured the user callback is invoked with the user - * cookie as a parameter. This loop continues until stopCapture() is called. Notice: since the callback is invoked for every packet burst - * using this method can be slower than using worker threads. On the other hand, it's a simpler way comparing to worker threads - * @param[in] onPacketsArrive The user callback which will be invoked each time a packet burst is captured by the device - * @param[in] onPacketsArriveUserCookie The user callback is invoked with this cookie as a parameter. It can be used to pass - * information from the user application to the callback - * @return True if capture thread started successfully or false if device is already in capture mode, number of opened RX queues isn't equal - * to 1, if the method couldn't find an available core to allocate for the capture thread, or if thread invocation failed. In - * all of these cases an appropriate error message will be printed - */ - bool startCaptureSingleThread(OnDpdkPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie); - - /** - * This method does exactly what startCaptureSingleThread() does, but with more than one RX queue / capturing thread. It's called - * with a core mask as a parameter and creates a packet capture thread on every core. Each capturing thread is assigned with a specific - * RX queue. This method assumes all cores in the core-mask are available and there are enough opened RX queues to match for each thread. - * If these assumptions are not true an error is returned. After invoking all threads, all of them run in an endless loop - * and try to capture packets from their designated RX queues. Each time a burst of packets is captured the callback is invoked with the user - * cookie and the thread ID that captured the packets - * @param[in] onPacketsArrive The user callback which will be invoked each time a burst of packets is captured by the device - * @param[in] onPacketsArriveUserCookie The user callback is invoked with this cookie as a parameter. It can be used to pass - * information from the user application to the callback - * @param coreMask The core-mask for creating the cpature threads - * @return True if all capture threads started successfully or false if device is already in capture mode, not all cores in the core-mask are - * available to DPDK, there are not enough opened RX queues to match all cores in the core-mask, or if thread invocation failed. In - * all of these cases an appropriate error message will be printed - */ - bool startCaptureMultiThreads(OnDpdkPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie, CoreMask coreMask); - - /** - * If device is in capture mode started by invoking startCaptureSingleThread() or startCaptureMultiThreads(), this method - * will stop all capturing threads and set the device to non-capturing mode - */ - void stopCapture(); - - /** - * @return The number of free mbufs in device's mbufs pool - */ - int getAmountOfFreeMbufs() const; - - /** - * @return The number of mbufs currently in use in device's mbufs pool - */ - int getAmountOfMbufsInUse() const; - - /** - * Retrieve RX/TX statistics from device - * @param[out] stats A reference to a DpdkDeviceStats object where stats will be written into - */ - void getStatistics(DpdkDeviceStats& stats) const; - - /** - * Clear device statistics - */ - void clearStatistics(); - - /** - * DPDK supports an option to buffer TX packets and send them only when reaching a certain threshold. This method enables - * the user to flush a TX buffer for certain TX queue and send the packets stored in it (you can read about it here: - * http://dpdk.org/doc/api/rte__ethdev_8h.html#a0e941a74ae1b1b886764bc282458d946). It has the option to flush only - * when timeout that was set in DpdkDeviceConfiguration#flushTxBufferTimeout expired or flush immediately regardless - * of the timeout. The usage of this method can be in the main loop where you can call this method once every a couple - * of iterations to make sure TX buffers are flushed - * @param[in] flushOnlyIfTimeoutExpired When set to true, flush will happen only if the timeout defined in - * DpdkDeviceConfiguration#flushTxBufferTimeout expired. If set to false flush will happen immediately. Default value - * is false - * @param[in] txQueueId The TX queue ID to flush its buffer. Default is 0 - * @return The number of packets sent after buffer was flushed - */ - uint16_t flushTxBuffer(bool flushOnlyIfTimeoutExpired = false, uint16_t txQueueId = 0); - - /** - * Check whether a specific RSS hash function is supported by this device (PMD) - * @param[in] rssHF RSS hash function to check - * @return True if this hash function is supported, false otherwise - */ - bool isDeviceSupportRssHashFunction(DpdkRssHashFunction rssHF) const; - - /** - * Check whether a mask of RSS hash functions is supported by this device (PMD) - * @param[in] rssHFMask RSS hash functions mask to check. This mask should be built from values in DpdkRssHashFunction enum - * @return True if all hash functions in this mask are supported, false otherwise - */ - bool isDeviceSupportRssHashFunction(uint64_t rssHFMask) const; - - /** - * @return A mask of all RSS hash functions supported by this device (PMD). This mask is built from values in DpdkRssHashFunction enum. - * Value of zero means RSS is not supported by this device - */ - uint64_t getSupportedRssHashFunctions() const; - - /** - * @return The RSS hash function mask configured for this device (PMD) - */ - uint64_t getConfiguredRssHashFunction() const; - - /** - * Translate RSS hash function mask to a list of their string representation - * @param rssHFMask RSS hash function mask - * @return RSS hash functions as strings - */ - std::vector rssHashFunctionMaskToString(uint64_t rssHFMask) const; - - //overridden methods - - /** - * Overridden method from IPcapDevice. It calls openMultiQueues() with 1 RX queue and 1 TX queue. - * Notice opening the device only makes it ready to use, it doesn't start packet capturing. The device is opened in promiscuous mode - * @return True if the device was opened successfully, false if device is already opened, if RX/TX queues configuration failed or of DPDK port - * configuration and startup failed - */ - bool open() { return openMultiQueues(1, 1); }; - - /** - * Close the DpdkDevice. When device is closed it's not possible work with it - */ - void close(); - - private: - - struct DpdkCoreConfiguration - { - int RxQueueId; - bool IsCoreInUse; - - void clear() { RxQueueId = -1; IsCoreInUse = false; } - - DpdkCoreConfiguration() : RxQueueId(-1), IsCoreInUse(false) {} - }; - - DpdkDevice(int port, uint32_t mBufPoolSize); - bool initMemPool(struct rte_mempool*& memPool, const char* mempoolName, uint32_t mBufPoolSize); - - bool configurePort(uint8_t numOfRxQueues, uint8_t numOfTxQueues); - bool initQueues(uint8_t numOfRxQueuesToInit, uint8_t numOfTxQueuesToInit); - bool startDevice(); - - static int dpdkCaptureThreadMain(void* ptr); - - void clearCoreConfiguration(); - bool initCoreConfigurationByCoreMask(CoreMask coreMask); - int getCoresInUseCount() const; - - void setDeviceInfo(); - - typedef rte_mbuf* (*PacketIterator)(void* packetStorage, int index); - uint16_t sendPacketsInner(uint16_t txQueueId, void* packetStorage, PacketIterator iter, int arrLength, bool useTxBuffer); - - uint64_t convertRssHfToDpdkRssHf(uint64_t rssHF) const; - uint64_t convertDpdkRssHfToRssHf(uint64_t dpdkRssHF) const; - - std::string m_DeviceName; - DpdkPMDType m_PMDType; - std::string m_PMDName; - std::string m_PciAddress; - - DpdkDeviceConfiguration m_Config; - - int m_Id; - MacAddress m_MacAddress; - uint16_t m_DeviceMtu; - struct rte_mempool* m_MBufMempool; - struct rte_eth_dev_tx_buffer** m_TxBuffers; - uint64_t m_TxBufferDrainTsc; - uint64_t* m_TxBufferLastDrainTsc; - DpdkCoreConfiguration m_CoreConfiguration[MAX_NUM_OF_CORES]; - uint16_t m_TotalAvailableRxQueues; - uint16_t m_TotalAvailableTxQueues; - uint16_t m_NumOfRxQueuesOpened; - uint16_t m_NumOfTxQueuesOpened; - OnDpdkPacketsArriveCallback m_OnPacketsArriveCallback; - void* m_OnPacketsArriveUserCookie; - bool m_StopThread; - - bool m_WasOpened; - - // RSS key used by the NIC for load balancing the packets between cores - static uint8_t m_RSSKey[40]; - - mutable DpdkDeviceStats m_PrevStats; - }; - -} // namespace pcpp - -#endif /* PCAPPP_DPDK_DEVICE */ +#ifndef PCAPPP_DPDK_DEVICE +#define PCAPPP_DPDK_DEVICE + +#include +#include +#include +#include "MacAddress.h" +#include "SystemUtils.h" +#include "Device.h" +#include "MBufRawPacket.h" + +/** + * @file + * This file and DpdkDeviceList.h provide PcapPlusPlus C++ wrapper for DPDK (stands for data-plan development kit). What is + * DPDK? as quoting from http://dpdk.org: "DPDK is a set of libraries and drivers for fast packet processing... These libraries can be used to: + * receive and send packets within the minimum number of CPU cycles (usually less than 80 cycles)... develop fast packet capture algorithms + * (tcpdump-like)... run third-party fast path stacks... Some packet processing functions have been benchmarked up to hundreds million + * frames per second, using 64-byte packets with a PCIe NIC"
+ * As DPDK API is written in C, PcapPlusPlus wraps the main functionality in a C++ easy-to-use classes which should have minimum affect + * on performance and packet processing rate. In addition it brings DPDK to the PcapPlusPlus framework and API so you can use DPDK + * together with other PcapPlusPlus features such as packet parsing and editing, etc.
+ * So how DPDK basically works? in order to boost packet processing performance on a commodity server DPDK is bypassing the Linux kernel. + * All the packet processing activity happens in the user space so basically packets are delivered from NIC hardware queues directly + * to user-space shared memory without going through the kernel. In addition DPDK uses polling instead of handling interrupts for each + * arrived packet (as interrupts create some delays). Other methods to boost packets processing implemented by DPDK are using Hugepages to + * decrease the size of TLB that results in a much faster virtual to physical page conversion, thread affinity to bind threads to a + * specific core, lock-free user-space multi-core synchronization using rings data structures and NUMA awareness to avoid expensive data + * transfers between sockets.
+ * Not every NIC supports kernel-bypass capabilities so DPDK cannot work with any NIC. The list of supported NICs are in DPDK's web-site + * http://dpdk.org/doc/nics . For each such NIC the DPDK framework provides a module that called poll-mode-driver (PMD in short) that + * enables this NIC to the working with DPDK. PcapPlusPlus wasn't tested with most PMDs but all of them should theoretically work as + * PcapPlusPlus doesn't change the PMD behavior
+ * DPDK has another basic data-structure called mbuf. An mbuf is DPDK wrapper struct for network packets. When working with packets + * in DPDK you actually work with mbufs. The mbuf contains the packet data (obviously) but also some metadata on the packet such + * as the DPDK port it was captured on, packet ref-count (which allows it to be referenced by several objects), etc. One important + * concept is that DPDK doesn't allocate mbufs on-the-fly but uses mbuf pools. These pools is allocated on application startup and + * used throughout the application. The goal of this, of course, is increasing packet processing performance as allocating memory has + * its cost. So pool size is important and varies between applications. For example: an application that stores packets in memory + * has to have a large pool of mbufs so mbufs doesn't run-out. PcapPlusPlus enables to choose the pool size at startup
+ *
+ * PcapPlusPlus main wrapper classes for DPDK are: + * - DpdkDevice - a class that wraps a DPDK port and provides all capabilities of receiving and sending packets to this port + * - DpdkDeviceList - a singleton class that initializes the DPDK infrastructure and creates DpdkDevice instances to all available ports. + * In addition it allows starting and stopping of worker threads + * - MBufRawPacket - a child class to RawPacket which customizes it for working with mbuf + * - In addition PcapPlusPlus provides a shell script to initialize DPDK prerequisites: setup_dpdk.py. This is an easy-to-use script + * that sets up huge-pages, loads DPDK kernel module and sets up the NICs that will be used by DPDK. This script must run before an + * application that uses DPDK runs. If you forgot to run it the application will fail with an appropriate error that will remind + * + * DPDK initialization using PcapPlusPlus: + * - Before application runs: run the setup_dpdk.py script + * - On application startup call DpdkDeviceList#initDpdk() static method to initialize DPDK infrastructure and DpdkDevice instances + * - Open the relevant DpdkDevice(s) + * - Send & receive packets... + */ + +struct rte_mbuf; +struct rte_mempool; +struct rte_eth_conf; +struct rte_eth_dev_tx_buffer; + +/** +* \namespace pcpp +* \brief The main namespace for the PcapPlusPlus lib +*/ +namespace pcpp +{ + +#define DPDK_MAX_RX_QUEUES 16 +#define DPDK_MAX_TX_QUEUES 16 +#define PCPP_RSS_HASH_MAGIC_NUMBER 0x123456 + + class DpdkDeviceList; + class DpdkDevice; + + /** + * An enum describing all PMD (poll mode driver) types supported by DPDK. For more info about these PMDs please visit the DPDK web-site + */ + enum DpdkPMDType + { + /** Unknown PMD type */ + PMD_UNKNOWN, + /** Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple (slave) NICs into a single logical interface*/ + PMD_BOND, + /** Intel E1000 PMD */ + PMD_E1000EM, + /** Intel 1GbE PMD */ + PMD_IGB, + /** Intel 1GbE virtual function PMD */ + PMD_IGBVF, + /** Cisco enic (UCS Virtual Interface Card) PMD */ + PMD_ENIC, + /** Intel fm10k PMD */ + PMD_FM10K, + /** Intel 40GbE PMD */ + PMD_I40E, + /** Intel 40GbE virtual function PMD */ + PMD_I40EVF, + /** Intel 10GbE PMD */ + PMD_IXGBE, + /** Intel 10GbE virtual function PMD */ + PMD_IXGBEVF, + /** Mellanox ConnectX-3, ConnectX-3 Pro PMD */ + PMD_MLX4, + /** Null PMD */ + PMD_NULL, + /** pcap file PMD */ + PMD_PCAP, + /** ring-based (memory) PMD */ + PMD_RING, + /** VirtIO PMD */ + PMD_VIRTIO, + /** VMWare VMXNET3 PMD */ + PMD_VMXNET3, + /** Xen Project PMD */ + PMD_XENVIRT, + /** AF_PACKET PMD */ + PMD_AF_PACKET + }; + + /** + * @typedef OnDpdkPacketsArriveCallback + * A callback that is called when a burst of packets are captured by DpdkDevice + * @param[in] packets A pointer to an array of MBufRawPacket + * @param[in] numOfPackets The length of the array + * @param[in] threadId The thread/core ID who captured the packets + * @param[in] device A pointer to the DpdkDevice who captured the packets + * @param[in] userCookie The user cookie assigned by the user in DpdkDevice#startCaptureSingleThread() or DpdkDevice#startCaptureMultiThreads + */ + typedef void (*OnDpdkPacketsArriveCallback)(MBufRawPacket* packets, uint32_t numOfPackets, uint8_t threadId, DpdkDevice* device, void* userCookie); + + /** + * @class DpdkDevice + * Encapsulates a DPDK port and enables receiving and sending packets using DPDK as well as getting interface info & status, packet + * statistics, etc. This class has no public c'tor as it's constructed by DpdkDeviceList during initialization.
+ * + * __RX/TX queues__: modern NICs provide hardware load-balancing for packets. This means that each packet received by the NIC is hashed + * by one or more parameter (IP address, port, etc.) and goes into one of several RX queues provided by the NIC. This enables + * applications to work in a multi-core environment where each core can read packets from different RX queue(s). Same goes for TX + * queues: it's possible to write packets to different TX queues and the NIC is taking care of sending them to the network. + * Different NICs provide different number of RX and TX queues. DPDK supports this capability and enables the user to open the + * DPDK port (DpdkDevice) with a single or multiple RX and TX queues. When receiving packets the user can decide from which RX queue + * to read from, and when transmitting packets the user can decide to which TX queue to send them to. RX/TX queues are configured + * when opening the DpdkDevice (see openMultiQueues())
+ * + * __Capturing packets__: there are two ways to capture packets using DpdkDevice: + * - using worker threads (see DpdkDeviceList#startDpdkWorkerThreads() ). When using this method the worker should use the + * DpdkDevice#receivePackets() methods to get packets from the DpdkDevice + * - by setting a callback which is invoked each time a burst of packets arrives. For more details see + * DpdkDevice#startCaptureSingleThread() + * + * __Sending packets:__ DpdkDevice has various methods for sending packets. They enable sending raw packets, parsed packets, etc. + * for all opened TX queues. Also, DPDK provides an option to buffer TX packets and send them only when reaching a certain threshold (you + * can read more about it here: http://dpdk.org/doc/api/rte__ethdev_8h.html#a0e941a74ae1b1b886764bc282458d946). DpdkDevice supports that + * option as well. See DpdkDevice#sendPackets()
+ * + * __Get interface info__: DpdkDevice provides all kind of information on the interface/device such as MAC address, MTU, link status, + * PCI address, PMD (poll-mode-driver) used for this port, etc. In addition it provides RX/TX statistics when receiving or sending + * packets
+ * + * __Known limitations:__ + * - BPF filters are currently not supported by this device (as opposed to other PcapPlusPlus device types. This means that the + * device cannot filter packets before they get to the user + * - It's not possible to set or change NIC load-balancing method. DPDK provides this capability but it's still not + * supported by DpdkDevice + */ + class DpdkDevice : public IDevice + { + friend class DpdkDeviceList; + friend class MBufRawPacket; + public: + + /** + * An enum describing all RSS (Receive Side Scaling) hash functions supported in DPDK. Notice not all + * PMDs support all types of hash functions + */ + enum DpdkRssHashFunction + { + /** No RSS */ + RSS_NONE = 0, + /** IPv4 based flow */ + RSS_IPV4 = 0x1, + /** Fragmented IPv4 based flow */ + RSS_FRAG_IPV4 = 0x2, + /** Non-fragmented IPv4 + TCP flow */ + RSS_NONFRAG_IPV4_TCP = 0x4, + /** Non-fragmented IPv4 + UDP flow */ + RSS_NONFRAG_IPV4_UDP = 0x8, + /** Non-fragmented IPv4 + SCTP flow */ + RSS_NONFRAG_IPV4_SCTP = 0x10, + /** Non-fragmented IPv4 + non TCP/UDP/SCTP flow */ + RSS_NONFRAG_IPV4_OTHER = 0x20, + /** IPv6 based flow */ + RSS_IPV6 = 0x40, + /** Fragmented IPv6 based flow */ + RSS_FRAG_IPV6 = 0x80, + /** Non-fragmented IPv6 + TCP flow */ + RSS_NONFRAG_IPV6_TCP = 0x100, + /** Non-fragmented IPv6 + UDP flow */ + RSS_NONFRAG_IPV6_UDP = 0x200, + /** Non-fragmented IPv6 + SCTP flow */ + RSS_NONFRAG_IPV6_SCTP = 0x400, + /** Non-fragmented IPv6 + non TCP/UDP/SCTP flow */ + RSS_NONFRAG_IPV6_OTHER = 0x800, + /** L2 payload based flow */ + RSS_L2_PAYLOAD = 0x1000, + /** IPv6 Ex based flow */ + RSS_IPV6_EX = 0x2000, + /** IPv6 + TCP Ex based flow */ + RSS_IPV6_TCP_EX = 0x4000, + /** IPv6 + UDP Ex based flow */ + RSS_IPV6_UDP_EX = 0x8000, + /** Consider device port number as a flow differentiator */ + RSS_PORT = 0x10000, + /** VXLAN protocol based flow */ + RSS_VXLAN = 0x20000, + /** GENEVE protocol based flow */ + RSS_GENEVE = 0x40000, + /** NVGRE protocol based flow */ + RSS_NVGRE = 0x80000, + /** All RSS functions supported by the device */ + RSS_ALL_SUPPORTED = -1, + /** A default set of RSS functions supported by the device */ + RSS_DEFAULT = PCPP_RSS_HASH_MAGIC_NUMBER + }; + + /** + * @struct DpdkDeviceConfiguration + * A struct that contains user configurable parameters for opening a DpdkDevice. All of these parameters have default values so + * the user doesn't have to use these parameters or understand exactly what is their effect + */ + struct DpdkDeviceConfiguration + { + /** + * When configuring a DPDK RX queue, DPDK creates descriptors it will use for receiving packets from the network to this RX queue. + * This parameter enables to configure the number of descriptors that will be created for each RX queue + */ + uint16_t receiveDescriptorsNumber; + + /** + * When configuring a DPDK TX queue, DPDK creates descriptors it will use for transmitting packets to the network through this TX queue. + * This parameter enables to configure the number of descriptors that will be created for each TX queue + */ + uint16_t transmitDescriptorsNumber; + + /** + * Set the TX buffer flush timeout in millisecond (only relevant if sending packets using DPDK TX buffer mechanism). + * A value of zero means no timeout + */ + uint16_t flushTxBufferTimeout; + + /** + * When configuring a DPDK device, DPDK supports to activate the Receive Side Scaling (RSS) feature to distribute traffic between the RX queues + * This parameter points to an array holding the RSS key to use for hashing specific header fields of received packets. + * The length of this array should be indicated by rssKeyLength below. + * Supplying a NULL value causes a default random hash key to be used by the device driver + */ + uint8_t* rssKey; + + /** + * This parameter indicates the length in bytes of the array pointed by rssKey. + * This length will be checked in i40e only. Others assume 40 bytes to be used. + */ + uint8_t rssKeyLength; + + /** + * This parameter enables to configure the types of packets to which the RSS hashing must be applied. The value + * is a mask composed of hash functions described in DpdkRssHashFunction enum. Supplying a value equal to zero + * disables the RSS feature. Supplying a value equal to -1 enables all hash functions supported by this PMD + */ + uint64_t rssHashFunction; + + /** + * A c'tor for this struct + * @param[in] receiveDescriptorsNumber An optional parameter for defining the number of RX descriptors that will be allocated for each RX queue. + * Default value is 128 + * @param[in] transmitDescriptorsNumber An optional parameter for defining the number of TX descriptors that will be allocated for each TX queue. + * Default value is 512 + * @param[in] flushTxBufferTimeout An optional parameter for setting TX buffer timeout in usec. Default value is 100 usec + * @param[in] rssHashFunction This parameter enable to configure the types of packets to which the RSS hashing must be applied. + * The value provided here should be a mask composed of hash functions described in DpdkRssHashFunction enum. + * The default value is RSS_DEFAULT. + * @param[in] rssKey A pointer to an array holding the RSS key to use for hashing specific header of received packets. If not + * specified, there is a default key defined inside DpdkDevice + * @param[in] rssKeyLength The length in bytes of the array pointed by rssKey. Default value is the length of default rssKey + */ + explicit DpdkDeviceConfiguration(uint16_t receiveDescriptorsNumber = 128, + uint16_t transmitDescriptorsNumber = 512, + uint16_t flushTxBufferTimeout = 100, + uint64_t rssHashFunction = RSS_DEFAULT, + uint8_t* rssKey = DpdkDevice::m_RSSKey, + uint8_t rssKeyLength = 40) + { + this->receiveDescriptorsNumber = receiveDescriptorsNumber; + this->transmitDescriptorsNumber = transmitDescriptorsNumber; + this->flushTxBufferTimeout = flushTxBufferTimeout; + this->rssKey = rssKey; + this->rssKeyLength = rssKeyLength; + this->rssHashFunction = rssHashFunction; + } + }; + + /** + * @struct LinkStatus + * A struct that contains the link status of a DpdkDevice (DPDK port). Returned from DpdkDevice#getLinkStatus() + */ + struct LinkStatus + { + /** Enum for describing link duplex */ + enum LinkDuplex + { + /** Full duplex */ + FULL_DUPLEX, + /** Half duplex */ + HALF_DUPLEX + }; + + /** True if link is up, false if it's down */ + bool linkUp; + /** Link speed in Mbps (for example: 10Gbe will show 10000) */ + int linkSpeedMbps; + /** Link duplex (half/full duplex) */ + LinkDuplex linkDuplex; + }; + + /** + * @struct RxTxStats + * A container for RX/TX statistics + */ + struct RxTxStats + { + /** Total number of packets */ + uint64_t packets; + /** Total number of successfully received bytes */ + uint64_t bytes; + /** Packets per second */ + uint64_t packetsPerSec; + /** Bytes per second */ + uint64_t bytesPerSec; + }; + + /** + * @struct DpdkDeviceStats + * A container for DpdkDevice statistics + */ + struct DpdkDeviceStats + { + /** DpdkDevice ID */ + uint8_t devId; + /** The timestamp of when the stats were written */ + timespec timestamp; + /** RX statistics per RX queue */ + RxTxStats rxStats[DPDK_MAX_RX_QUEUES]; + /** TX statistics per TX queue */ + RxTxStats txStats[DPDK_MAX_RX_QUEUES]; + /** RX statistics, aggregated for all RX queues */ + RxTxStats aggregatedRxStats; + /** TX statistics, aggregated for all TX queues */ + RxTxStats aggregatedTxStats; + /** Total number of RX packets dropped by H/W because there are no available buffers (i.e RX queues are full) */ + uint64_t rxPacketsDroppedByHW; + /** Total number of erroneous packets */ + uint64_t rxErroneousPackets; + /** Total number of RX mbuf allocation failuers */ + uint64_t rxMbufAlocFailed; + }; + + virtual ~DpdkDevice(); + + /** + * @return The device ID (DPDK port ID) + */ + int getDeviceId() const { return m_Id; } + /** + * @return The device name which is in the format of 'DPDK_[PORT-ID]' + */ + std::string getDeviceName() const { return m_DeviceName; } + + /** + * @return The MAC address of the device (DPDK port) + */ + MacAddress getMacAddress() const { return m_MacAddress; } + + /** + * @return The name of the PMD (poll mode driver) DPDK is using for this device. You can read about PMDs in the DPDK documentation: + * http://dpdk.org/doc/guides/prog_guide/poll_mode_drv.html + */ + std::string getPMDName() const { return m_PMDName; } + + /** + * @return The enum type of the PMD (poll mode driver) DPDK is using for this device. You can read about PMDs in the DPDK documentation: + * http://dpdk.org/doc/guides/prog_guide/poll_mode_drv.html + */ + DpdkPMDType getPMDType() const { return m_PMDType; } + + /** + * @return The PCI address of the device + */ + std::string getPciAddress() const { return m_PciAddress; } + + /** + * @return The device's maximum transmission unit (MTU) in bytes + */ + uint16_t getMtu() const { return m_DeviceMtu; } + + /** + * Set a new maximum transmission unit (MTU) for this device + * @param[in] newMtu The new MTU in bytes + * @return True if MTU was set successfully, false if operation failed or if PMD doesn't support changing the MTU + */ + bool setMtu(uint16_t newMtu); + + /** + * @return True if this device is a virtual interface (such as VMXNET3, 1G/10G virtual function, etc.), false otherwise + */ + bool isVirtual() const; + + /** + * Get the link status (link up/down, link speed and link duplex) + * @param[out] linkStatus A reference to object the result shall be written to + */ + void getLinkStatus(LinkStatus& linkStatus) const; + + /** + * @return The core ID used in this context + */ + uint32_t getCurrentCoreId() const; + + /** + * @return The number of RX queues currently opened for this device (as configured in openMultiQueues() ) + */ + uint16_t getNumOfOpenedRxQueues() const { return m_NumOfRxQueuesOpened; } + + /** + * @return The number of TX queues currently opened for this device (as configured in openMultiQueues() ) + */ + uint16_t getNumOfOpenedTxQueues() const { return m_NumOfTxQueuesOpened; } + + /** + * @return The total number of RX queues available on this device + */ + uint16_t getTotalNumOfRxQueues() const { return m_TotalAvailableRxQueues; } + + /** + * @return The total number of TX queues available on this device + */ + uint16_t getTotalNumOfTxQueues() const { return m_TotalAvailableTxQueues; } + + + /** + * Receive raw packets from the network + * @param[out] rawPacketsArr A vector where all received packets will be written into + * @param[in] rxQueueId The RX queue to receive packets from + * @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log + */ + uint16_t receivePackets(MBufRawPacketVector& rawPacketsArr, uint16_t rxQueueId) const; + + /** + * Receive raw packets from the network. Please notice that in terms of performance, this is the best method to use + * for receiving packets because out of all receivePackets overloads this method requires the least overhead and is + * almost as efficient as receiving packets directly through DPDK. So if performance is a critical factor in your + * application, please use this method + * @param[out] rawPacketsArr A pointer to an array of MBufRawPacket pointers where all received packets will be written into. The array is expected to + * be allocated by the user and its length should be provided in rawPacketArrLength. Number of packets received will be returned. + * Notice it's the user responsibility to free the array and its content when done using it + * @param[out] rawPacketArrLength The length of MBufRawPacket pointers array + * @param[in] rxQueueId The RX queue to receive packets from + * @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log + */ + uint16_t receivePackets(MBufRawPacket** rawPacketsArr, uint16_t rawPacketArrLength, uint16_t rxQueueId) const; + + /** + * Receive parsed packets from the network + * @param[out] packetsArr A pointer to an allocated array of Packet pointers where all received packets will be written into. The array is expected to + * be allocated by the user and its length should be provided in packetsArrLength. Number of packets received will be returned. + * Notice it's the user responsibility to free the array and its content when done using it + * @param[out] packetsArrLength The length of Packet pointers array + * @param[in] rxQueueId The RX queue to receive packets from + * @return The number of packets received. If an error occurred 0 will be returned and the error will be printed to log + */ + uint16_t receivePackets(Packet** packetsArr, uint16_t packetsArrLength, uint16_t rxQueueId) const; + + /** + * Send an array of MBufRawPacket to the network. Please notice the following:
+ * - In terms of performance, this is the best method to use for sending packets because out of all sendPackets overloads + * this method requires the least overhead and is almost as efficient as sending the packets directly through DPDK. So if performance + * is a critical factor in your application, please use this method + * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each + * iteration of 64 packets + * - If the number of packets to send is higher than a threshold of 80% of total TX descriptors (which is typically around 400 packets), + * then after reaching this threshold there is a built-in 0.2 sec sleep to let the TX descriptors clean + * - The mbufs used in this method aren't freed by this method, they will be transparently freed by DPDK + *

+ * @param[in] rawPacketsArr A pointer to an array of MBufRawPacket + * @param[in] arrLength The length of the array + * @param[in] txQueueId An optional parameter which indicates to which TX queue the packets will be sent to. The default is + * TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's + * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) + * @return The number of packets actually and successfully sent. If device is not opened or TX queue isn't open, 0 will be returned. + * Also, if TX buffer is being used and packets are buffered, some or all may not be actually sent + */ + uint16_t sendPackets(MBufRawPacket** rawPacketsArr, uint16_t arrLength, uint16_t txQueueId = 0, bool useTxBuffer = false); + + /** + * Send an array of parsed packets to the network. Please notice the following:
+ * - If some or all of the packets contain raw packets which aren't of type MBufRawPacket, a new temp MBufRawPacket instances + * will be created and packet data will be copied to them. This is necessary to allocate mbufs which will store the data to be sent. + * If performance is a critical factor please make sure you send parsed packets that contain only raw packets of type MBufRawPacket + * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each + * iteration of 64 packets + * - If the number of packets to send is higher than a threshold of 80% of total TX descriptors (which is typically around 400 packets), + * then after reaching this threshold there is a built-in 0.2 sec sleep to let the TX descriptors clean + * - The mbufs used or allocated in this method aren't freed by this method, they will be transparently freed by DPDK + *

+ * @param[in] packetsArr A pointer to an array of parsed packet pointers + * @param[in] arrLength The length of the array + * @param[in] txQueueId An optional parameter which indicates to which TX queue the packets will be sent to. The default is + * TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's + * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) + * @return The number of packets actually and successfully sent. If device is not opened or TX queue isn't open, 0 will be returned. + * Also, if TX buffer is being used and packets are buffered, some or all may not be actually sent + */ + uint16_t sendPackets(Packet** packetsArr, uint16_t arrLength, uint16_t txQueueId = 0, bool useTxBuffer = false); + + /** + * Send a vector of MBufRawPacket pointers to the network. Please notice the following:
+ * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each + * iteration of 64 packets + * - If the number of packets to send is higher than a threshold of 80% of total TX descriptors (which is typically around 400 packets), + * then after reaching this threshold there is a built-in 0.2 sec sleep to let the TX descriptors clean + * - The mbufs used in this method aren't freed by this method, they will be transparently freed by DPDK + *

+ * @param[in] rawPacketsVec The vector of raw packet + * @param[in] txQueueId An optional parameter which indicates to which TX queue the packets will be sent to. The default is + * TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's + * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) + * @return The number of packets actually and successfully sent. If device is not opened or TX queue isn't open, 0 will be returned. + * Also, if TX buffer is being used and packets are buffered, some or all may not be actually sent + */ + uint16_t sendPackets(MBufRawPacketVector& rawPacketsVec, uint16_t txQueueId = 0, bool useTxBuffer = false); + + /** + * Send a vector of RawPacket pointers to the network. Please notice the following:
+ * - If some or all of the raw packets aren't of type MBufRawPacket, a new temp MBufRawPacket instances will be created + * and packet data will be copied to them. This is necessary to allocate mbufs which will store the data to be sent. If + * performance is a critical factor please make sure you send only raw packets of type MBufRawPacket (or use the sendPackets overload + * that sends MBufRawPacketVector) + * - If the number of packets to send is higher than 64 this method will run multiple iterations of sending packets to DPDK, each + * iteration of 64 packets + * - If the number of packets to send is higher than a threshold of 80% of total TX descriptors (which is typically around 400 packets), + * then after reaching this threshold there is a built-in 0.2 sec sleep to let the TX descriptors clean + * - The mbufs used or allocated in this method aren't freed by this method, they will be transparently freed by DPDK + *

+ * @param[in] rawPacketsVec The vector of raw packet + * @param[in] txQueueId An optional parameter which indicates to which TX queue the packets will be sent to. The default is + * TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's + * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) + * @return The number of packets actually and successfully sent. If device is not opened or TX queue isn't open, 0 will be returned. + * Also, if TX buffer is being used and packets are buffered, some or all may not be actually sent + */ + uint16_t sendPackets(RawPacketVector& rawPacketsVec, uint16_t txQueueId = 0, bool useTxBuffer = false); + + /** + * Send a raw packet to the network. Please notice that if the raw packet isn't of type MBufRawPacket, a new temp MBufRawPacket + * will be created and the data will be copied to it. This is necessary to allocate an mbuf which will store the data to be sent. + * If performance is a critical factor please make sure you send a raw packet of type MBufRawPacket. Please also notice that the + * mbuf used or allocated in this method isn't freed by this method, it will be transparently freed by DPDK + * @param[in] rawPacket The raw packet to send + * @param[in] txQueueId An optional parameter which indicates to which TX queue the packet will be sent to. The default is + * TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's + * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) + * @return True if packet was sent successfully or false if device is not opened, TX queue isn't opened, or if the packet wasn't + * sent for any other reason. Please notice that when using TX buffers the packet may be buffered and not sent immediately, which + * may also result in returning false + */ + bool sendPacket(RawPacket& rawPacket, uint16_t txQueueId = 0, bool useTxBuffer = false); + + /** + * Send a MBufRawPacket to the network. Please notice that the mbuf used in this method isn't freed by this method, it will be + * transparently freed by DPDK + * @param[in] rawPacket The MBufRawPacket to send + * @param[in] txQueueId An optional parameter which indicates to which TX queue the packet will be sent to. The default is + * TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's + * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) + * @return True if packet was sent successfully or false if device is not opened, TX queue isn't opened, or if the packet wasn't + * sent for any other reason. Please notice that when using TX buffers the packet may be buffered and not sent immediately, which + * may also result in returning false + */ + bool sendPacket(MBufRawPacket& rawPacket, uint16_t txQueueId = 0, bool useTxBuffer = false); + + /** + * Send a parsed packet to the network. Please notice that the mbuf used or allocated in this method isn't freed by this method, + * it will be transparently freed by DPDK + * @param[in] packet The parsed packet to send. Please notice that if the packet contains a raw packet which isn't of type + * MBufRawPacket, a new temp MBufRawPacket will be created and the data will be copied to it. This is necessary to + * allocate an mbuf which will store the data to be sent. If performance is a critical factor please make sure you send a + * parsed packet that contains a raw packet of type MBufRawPacket + * @param[in] txQueueId An optional parameter which indicates to which TX queue the packet will be sent on. The default is + * TX queue 0 + * @param[in] useTxBuffer A flag which indicates whether to use TX buffer mechanism or not. To read more about DPDK's + * TX buffer mechanism please refer to DpdkDevice class description. Default value is false (don't use this mechanism) + * @return True if packet was sent successfully or false if device is not opened, TX queue isn't opened, or if the packet wasn't + * sent for any other reason. Please notice that when using TX buffers the packet may be buffered and not sent immediately, which + * may also result in returning false + */ + bool sendPacket(Packet& packet, uint16_t txQueueId = 0, bool useTxBuffer = false); + + /** + * Overridden method from IPcapDevice. __BPF filters are currently not implemented for DpdkDevice__ + * @param[in] filter Not used in this method + * @return Always false with a "Filters aren't supported in DPDK device" error message + */ + bool setFilter(GeneralFilter& filter); + + /** + * Overridden method from IPcapDevice. __BPF filters are currently not implemented for DpdkDevice__ + * @param[in] filterAsString Not used in this method + * @return Always false with a "Filters aren't supported in DPDK device" error message + */ + bool setFilter(std::string filterAsString); + + /** + * Open the DPDK device. Notice opening the device only makes it ready to use, it doesn't start packet capturing. This method initializes RX and TX queues, + * configures the DPDK port and starts it. Call close() to close the device. The device is opened in promiscuous mode + * @param[in] numOfRxQueuesToOpen Number of RX queues to setup. This number must be smaller or equal to the return value of getTotalNumOfRxQueues() + * @param[in] numOfTxQueuesToOpen Number of TX queues to setup. This number must be smaller or equal to the return value of getTotalNumOfTxQueues() + * @param[in] config Optional parameter for defining special port configuration parameters such as number of receive/transmit descriptors. If not set the default + * parameters will be set (see DpdkDeviceConfiguration) + * @return True if the device was opened successfully, false if device is already opened, if RX/TX queues configuration failed or of DPDK port + * configuration and startup failed + */ + bool openMultiQueues(uint16_t numOfRxQueuesToOpen, uint16_t numOfTxQueuesToOpen, const DpdkDeviceConfiguration& config = DpdkDeviceConfiguration()); + + /** + * There are two ways to capture packets using DpdkDevice: one of them is using worker threads (see DpdkDeviceList#startDpdkWorkerThreads() ) and + * the other way is setting a callback which is invoked each time a burst of packets is captured. This method implements the second way. + * After invoking this method the DpdkDevice enters capture mode and starts capturing packets. + * This method assumes there is only 1 RX queue opened for this device, otherwise an error is returned. It then allocates a core and creates 1 thread + * that runs in an endless loop and tries to capture packets using DPDK. Each time a burst of packets is captured the user callback is invoked with the user + * cookie as a parameter. This loop continues until stopCapture() is called. Notice: since the callback is invoked for every packet burst + * using this method can be slower than using worker threads. On the other hand, it's a simpler way comparing to worker threads + * @param[in] onPacketsArrive The user callback which will be invoked each time a packet burst is captured by the device + * @param[in] onPacketsArriveUserCookie The user callback is invoked with this cookie as a parameter. It can be used to pass + * information from the user application to the callback + * @return True if capture thread started successfully or false if device is already in capture mode, number of opened RX queues isn't equal + * to 1, if the method couldn't find an available core to allocate for the capture thread, or if thread invocation failed. In + * all of these cases an appropriate error message will be printed + */ + bool startCaptureSingleThread(OnDpdkPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie); + + /** + * This method does exactly what startCaptureSingleThread() does, but with more than one RX queue / capturing thread. It's called + * with a core mask as a parameter and creates a packet capture thread on every core. Each capturing thread is assigned with a specific + * RX queue. This method assumes all cores in the core-mask are available and there are enough opened RX queues to match for each thread. + * If these assumptions are not true an error is returned. After invoking all threads, all of them run in an endless loop + * and try to capture packets from their designated RX queues. Each time a burst of packets is captured the callback is invoked with the user + * cookie and the thread ID that captured the packets + * @param[in] onPacketsArrive The user callback which will be invoked each time a burst of packets is captured by the device + * @param[in] onPacketsArriveUserCookie The user callback is invoked with this cookie as a parameter. It can be used to pass + * information from the user application to the callback + * @param coreMask The core-mask for creating the cpature threads + * @return True if all capture threads started successfully or false if device is already in capture mode, not all cores in the core-mask are + * available to DPDK, there are not enough opened RX queues to match all cores in the core-mask, or if thread invocation failed. In + * all of these cases an appropriate error message will be printed + */ + bool startCaptureMultiThreads(OnDpdkPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie, CoreMask coreMask); + + /** + * If device is in capture mode started by invoking startCaptureSingleThread() or startCaptureMultiThreads(), this method + * will stop all capturing threads and set the device to non-capturing mode + */ + void stopCapture(); + + /** + * @return The number of free mbufs in device's mbufs pool + */ + int getAmountOfFreeMbufs() const; + + /** + * @return The number of mbufs currently in use in device's mbufs pool + */ + int getAmountOfMbufsInUse() const; + + /** + * Retrieve RX/TX statistics from device + * @param[out] stats A reference to a DpdkDeviceStats object where stats will be written into + */ + void getStatistics(DpdkDeviceStats& stats) const; + + /** + * Clear device statistics + */ + void clearStatistics(); + + /** + * DPDK supports an option to buffer TX packets and send them only when reaching a certain threshold. This method enables + * the user to flush a TX buffer for certain TX queue and send the packets stored in it (you can read about it here: + * http://dpdk.org/doc/api/rte__ethdev_8h.html#a0e941a74ae1b1b886764bc282458d946). It has the option to flush only + * when timeout that was set in DpdkDeviceConfiguration#flushTxBufferTimeout expired or flush immediately regardless + * of the timeout. The usage of this method can be in the main loop where you can call this method once every a couple + * of iterations to make sure TX buffers are flushed + * @param[in] flushOnlyIfTimeoutExpired When set to true, flush will happen only if the timeout defined in + * DpdkDeviceConfiguration#flushTxBufferTimeout expired. If set to false flush will happen immediately. Default value + * is false + * @param[in] txQueueId The TX queue ID to flush its buffer. Default is 0 + * @return The number of packets sent after buffer was flushed + */ + uint16_t flushTxBuffer(bool flushOnlyIfTimeoutExpired = false, uint16_t txQueueId = 0); + + /** + * Check whether a specific RSS hash function is supported by this device (PMD) + * @param[in] rssHF RSS hash function to check + * @return True if this hash function is supported, false otherwise + */ + bool isDeviceSupportRssHashFunction(DpdkRssHashFunction rssHF) const; + + /** + * Check whether a mask of RSS hash functions is supported by this device (PMD) + * @param[in] rssHFMask RSS hash functions mask to check. This mask should be built from values in DpdkRssHashFunction enum + * @return True if all hash functions in this mask are supported, false otherwise + */ + bool isDeviceSupportRssHashFunction(uint64_t rssHFMask) const; + + /** + * @return A mask of all RSS hash functions supported by this device (PMD). This mask is built from values in DpdkRssHashFunction enum. + * Value of zero means RSS is not supported by this device + */ + uint64_t getSupportedRssHashFunctions() const; + + /** + * @return The RSS hash function mask configured for this device (PMD) + */ + uint64_t getConfiguredRssHashFunction() const; + + /** + * Translate RSS hash function mask to a list of their string representation + * @param rssHFMask RSS hash function mask + * @return RSS hash functions as strings + */ + std::vector rssHashFunctionMaskToString(uint64_t rssHFMask) const; + + //overridden methods + + /** + * Overridden method from IPcapDevice. It calls openMultiQueues() with 1 RX queue and 1 TX queue. + * Notice opening the device only makes it ready to use, it doesn't start packet capturing. The device is opened in promiscuous mode + * @return True if the device was opened successfully, false if device is already opened, if RX/TX queues configuration failed or of DPDK port + * configuration and startup failed + */ + bool open() { return openMultiQueues(1, 1); }; + + /** + * Close the DpdkDevice. When device is closed it's not possible work with it + */ + void close(); + + private: + + struct DpdkCoreConfiguration + { + int RxQueueId; + bool IsCoreInUse; + + void clear() { RxQueueId = -1; IsCoreInUse = false; } + + DpdkCoreConfiguration() : RxQueueId(-1), IsCoreInUse(false) {} + }; + + DpdkDevice(int port, uint32_t mBufPoolSize); + bool initMemPool(struct rte_mempool*& memPool, const char* mempoolName, uint32_t mBufPoolSize); + + bool configurePort(uint8_t numOfRxQueues, uint8_t numOfTxQueues); + bool initQueues(uint8_t numOfRxQueuesToInit, uint8_t numOfTxQueuesToInit); + bool startDevice(); + + static int dpdkCaptureThreadMain(void* ptr); + + void clearCoreConfiguration(); + bool initCoreConfigurationByCoreMask(CoreMask coreMask); + int getCoresInUseCount() const; + + void setDeviceInfo(); + + typedef rte_mbuf* (*PacketIterator)(void* packetStorage, int index); + uint16_t sendPacketsInner(uint16_t txQueueId, void* packetStorage, PacketIterator iter, int arrLength, bool useTxBuffer); + + uint64_t convertRssHfToDpdkRssHf(uint64_t rssHF) const; + uint64_t convertDpdkRssHfToRssHf(uint64_t dpdkRssHF) const; + + std::string m_DeviceName; + DpdkPMDType m_PMDType; + std::string m_PMDName; + std::string m_PciAddress; + + DpdkDeviceConfiguration m_Config; + + int m_Id; + MacAddress m_MacAddress; + uint16_t m_DeviceMtu; + struct rte_mempool* m_MBufMempool; + struct rte_eth_dev_tx_buffer** m_TxBuffers; + uint64_t m_TxBufferDrainTsc; + uint64_t* m_TxBufferLastDrainTsc; + DpdkCoreConfiguration m_CoreConfiguration[MAX_NUM_OF_CORES]; + uint16_t m_TotalAvailableRxQueues; + uint16_t m_TotalAvailableTxQueues; + uint16_t m_NumOfRxQueuesOpened; + uint16_t m_NumOfTxQueuesOpened; + OnDpdkPacketsArriveCallback m_OnPacketsArriveCallback; + void* m_OnPacketsArriveUserCookie; + bool m_StopThread; + + bool m_WasOpened; + + // RSS key used by the NIC for load balancing the packets between cores + static uint8_t m_RSSKey[40]; + + mutable DpdkDeviceStats m_PrevStats; + }; + +} // namespace pcpp + +#endif /* PCAPPP_DPDK_DEVICE */ diff --git a/Pcap++/header/DpdkDeviceList.h b/Pcap++/header/DpdkDeviceList.h index 7d2eb58055..6988e3d0ea 100644 --- a/Pcap++/header/DpdkDeviceList.h +++ b/Pcap++/header/DpdkDeviceList.h @@ -1,205 +1,205 @@ -#ifndef PCAPPP_DPDK_DEVICE_LIST -#define PCAPPP_DPDK_DEVICE_LIST - -#include "SystemUtils.h" -#include "DpdkDevice.h" -#include "Logger.h" -#include - -/** - * @file - * For details about PcapPlusPlus support for DPDK see DpdkDevice.h file description - */ - -/** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @class DpdkWorkerThread - * There are two ways to capture packets using DpdkDevice: one of them is using worker threads and the other way is using - * a callback which is invoked on each a burst of packets are captured (see DpdkDevice#startCaptureSingleThread() ). This class - * is a base class for implementing workers. A worker is basically a class that is activated by DpdkDeviceList#startDpdkWorkerThreads() - * and runs on a designated core. When it runs it can do whatever the user wants it to do. The most common use it running in an - * endless loop and receive, analyze and send packets using one or more DpdkDevice instances. It can do all kinds of processing for - * these packets. The only restriction for a worker class is that it must implement the 3 abstract methods stated in this class-interface - * for start running, stop running and get the core ID the worker is running on. - */ - class DpdkWorkerThread - { - public: - /** - * A virtual d'tor. Can be overridden by child class if needed - */ - virtual ~DpdkWorkerThread() {} - - /** - * An abstract method that must be implemented by child class. It's the indication for the worker to start running - * @param[in] coreId The core ID the worker is running on (should be returned in getCoreId() ) - * @return True if all went well or false otherwise - */ - virtual bool run(uint32_t coreId) = 0; - - /** - * An abstract method that must be implemented by child class. It's the indication for the worker to stop running. After - * this method is called the caller expects the worker to stop running as fast as possible - * @return No return value - */ - virtual void stop() = 0; - - /** - * An abstract method that must be implemented by child class. Get the core ID the worker is running on (as sent to the run() method - * as a parameter) - * @return The core ID the worker is running on - */ - virtual uint32_t getCoreId() const = 0; - }; - - class KniDeviceList; - - /** - * @class DpdkDeviceList - * A singleton class that encapsulates DPDK initialization and holds the list of DpdkDevice instances. As it's a singleton, it has only - * one active instance doesn't have a public c'tor. This class has several main uses: - * - it contains the initDpdk() static method which initializes the DPDK infrastructure. It should be called once in every application at - * its startup process - * - it contains the list of DpdkDevice instances and enables access to them - * - it has methods to start and stop worker threads. See more details in startDpdkWorkerThreads() - */ - class DpdkDeviceList - { - friend class KniDeviceList; - private: - bool m_IsInitialized; - static bool m_IsDpdkInitialized; - static uint32_t m_MBufPoolSizePerDevice; - static CoreMask m_CoreMask; - std::vector m_DpdkDeviceList; - std::vector m_WorkerThreads; - - DpdkDeviceList(); - - bool isInitialized() const { return (m_IsInitialized && m_IsDpdkInitialized); } - bool initDpdkDevices(uint32_t mBufPoolSizePerDevice); - static bool verifyHugePagesAndDpdkDriver(); - - static int dpdkWorkerThreadStart(void* ptr); - public: - - ~DpdkDeviceList(); - - /** - * As DpdkDeviceList is a singleton, this is the static getter to retrieve its instance. Note that if the static method - * initDpdk() was not called or returned false this instance won't be initialized and DpdkDevices won't be initialized either - * @return The singleton instance of DpdkDeviceList - */ - static DpdkDeviceList& getInstance() - { - static DpdkDeviceList instance; - if (!instance.isInitialized()) - instance.initDpdkDevices(DpdkDeviceList::m_MBufPoolSizePerDevice); - - return instance; - } - - /** - * A static method that has to be called once at the startup of every application that uses DPDK. It does several things: - * - verifies huge-pages are set and DPDK kernel module is loaded (these are set by the setup_dpdk.py external script that - * has to be run before application is started) - * - initializes the DPDK infrastructure - * - creates DpdkDevice instances for all ports available for DPDK - * - * @param[in] coreMask The cores to initialize DPDK with. After initialization, DPDK will only be able to use these cores - * for its work. The core mask should have a bit set for every core to use. For example: if the user want to use cores 1,2 - * the core mask should be 6 (binary: 110) - * @param[in] mBufPoolSizePerDevice The mbuf pool size each DpdkDevice will have. This has to be a number which is a power of 2 - * minus 1, for example: 1023 (= 2^10-1) or 4,294,967,295 (= 2^32-1), etc. This is a DPDK limitation, not PcapPlusPlus. - * The size of the mbuf pool size dictates how many packets can be handled by the application at the same time. For example: if - * pool size is 1023 it means that no more than 1023 packets can be handled or stored in application memory at every point in time - * @param[in] masterCore The core DPDK will use as master to control all worker thread. The default, unless set otherwise, is 0 - * @param[in] initDpdkArgc Number of optional arguments - * @param[in] initDpdkArgv Optional arguments - * @param[in] appName program name to be provided for the DPDK - * @return True if initialization succeeded or false if huge-pages or DPDK kernel driver are not loaded, if mBufPoolSizePerDevice - * isn't power of 2 minus 1, if DPDK infra initialization failed or if DpdkDevice initialization failed. Anyway, if this method - * returned false it's impossible to use DPDK with PcapPlusPlus. You can get some more details about mbufs and pools in - * DpdkDevice.h file description or in DPDK web site - */ - static bool initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice, uint8_t masterCore = 0, uint32_t initDpdkArgc = 0, char **initDpdkArgv = NULL, const std::string& appName = "pcapplusplusapp"); - - /** - * Get a DpdkDevice by port ID - * @param[in] portId The port ID - * @return A pointer to the DpdkDevice or NULL if no such device is found - */ - DpdkDevice* getDeviceByPort(int portId) const; - - /** - * Get a DpdkDevice by port PCI address - * @param[in] pciAddr The port PCI address - * @return A pointer to the DpdkDevice or NULL if no such device is found - */ - DpdkDevice* getDeviceByPciAddress(const std::string& pciAddr) const; - - /** - * @return A vector of all DpdkDevice instances - */ - const std::vector& getDpdkDeviceList() const { return m_DpdkDeviceList; } - - /** - * @return DPDK master core which is the core that initializes the application - */ - SystemCore getDpdkMasterCore() const; - - /** - * Change the log level of all modules of DPDK - * @param[in] logLevel The log level to set. Logger#Info is RTE_LOG_NOTICE and Logger#Debug is RTE_LOG_DEBUG - */ - void setDpdkLogLevel(Logger::LogLevel logLevel); - - /** - * @return The current DPDK log level. RTE_LOG_NOTICE and lower are considered as Logger#Info. RTE_LOG_INFO or RTE_LOG_DEBUG - * are considered as Logger#Debug - */ - Logger::LogLevel getDpdkLogLevel() const; - - /** - * Order DPDK to write all its logs to a file - * @param[in] logFile The file to write to - * @return True if action succeeded, false otherwise - */ - bool writeDpdkLogToFile(FILE* logFile); - - /** - * There are two ways to capture packets using DpdkDevice: one of them is using worker threads and the other way is setting - * a callback which is invoked each time a burst of packets is captured (see DpdkDevice#startCaptureSingleThread() ). This - * method implements the first way. See a detailed description of workers in DpdkWorkerThread class description. This method - * gets a vector of workers (classes that implement the DpdkWorkerThread interface) and a core mask and starts a worker thread - * on each core (meaning - call the worker's DpdkWorkerThread#run() method). Workers usually run in an endless loop and will - * be ordered to stop by calling stopDpdkWorkerThreads().
- * Note that number of cores in the core mask must be equal to the number of workers. In addition it's impossible to run a - * worker thread on DPDK master core, so the core mask shouldn't include the master core (you can find the master core by - * calling getDpdkMasterCore() ). - * @param[in] coreMask The bitmask of cores to run worker threads on. This list shouldn't include DPDK master core - * @param[in] workerThreadsVec A vector of worker instances to run (classes who implement the DpdkWorkerThread interface). - * Number of workers in this vector must be equal to the number of cores in the core mask. Notice that the instances of - * DpdkWorkerThread shouldn't be freed until calling stopDpdkWorkerThreads() as these instances are running - * @return True if all worker threads started successfully or false if: DPDK isn't initialized (initDpdk() wasn't called or - * returned false), number of cores differs from number of workers, core mask includes DPDK master core or if one of the - * worker threads couldn't be run - */ - bool startDpdkWorkerThreads(CoreMask coreMask, std::vector& workerThreadsVec); - - /** - * Assuming worker threads are running, this method orders them to stop by calling DpdkWorkerThread#stop(). Then it waits until - * they stop running - */ - void stopDpdkWorkerThreads(); - }; - -} // namespace pcpp - -#endif /* PCAPPP_DPDK_DEVICE_LIST */ +#ifndef PCAPPP_DPDK_DEVICE_LIST +#define PCAPPP_DPDK_DEVICE_LIST + +#include "SystemUtils.h" +#include "DpdkDevice.h" +#include "Logger.h" +#include + +/** + * @file + * For details about PcapPlusPlus support for DPDK see DpdkDevice.h file description + */ + +/** +* \namespace pcpp +* \brief The main namespace for the PcapPlusPlus lib +*/ +namespace pcpp +{ + + /** + * @class DpdkWorkerThread + * There are two ways to capture packets using DpdkDevice: one of them is using worker threads and the other way is using + * a callback which is invoked on each a burst of packets are captured (see DpdkDevice#startCaptureSingleThread() ). This class + * is a base class for implementing workers. A worker is basically a class that is activated by DpdkDeviceList#startDpdkWorkerThreads() + * and runs on a designated core. When it runs it can do whatever the user wants it to do. The most common use it running in an + * endless loop and receive, analyze and send packets using one or more DpdkDevice instances. It can do all kinds of processing for + * these packets. The only restriction for a worker class is that it must implement the 3 abstract methods stated in this class-interface + * for start running, stop running and get the core ID the worker is running on. + */ + class DpdkWorkerThread + { + public: + /** + * A virtual d'tor. Can be overridden by child class if needed + */ + virtual ~DpdkWorkerThread() {} + + /** + * An abstract method that must be implemented by child class. It's the indication for the worker to start running + * @param[in] coreId The core ID the worker is running on (should be returned in getCoreId() ) + * @return True if all went well or false otherwise + */ + virtual bool run(uint32_t coreId) = 0; + + /** + * An abstract method that must be implemented by child class. It's the indication for the worker to stop running. After + * this method is called the caller expects the worker to stop running as fast as possible + * @return No return value + */ + virtual void stop() = 0; + + /** + * An abstract method that must be implemented by child class. Get the core ID the worker is running on (as sent to the run() method + * as a parameter) + * @return The core ID the worker is running on + */ + virtual uint32_t getCoreId() const = 0; + }; + + class KniDeviceList; + + /** + * @class DpdkDeviceList + * A singleton class that encapsulates DPDK initialization and holds the list of DpdkDevice instances. As it's a singleton, it has only + * one active instance doesn't have a public c'tor. This class has several main uses: + * - it contains the initDpdk() static method which initializes the DPDK infrastructure. It should be called once in every application at + * its startup process + * - it contains the list of DpdkDevice instances and enables access to them + * - it has methods to start and stop worker threads. See more details in startDpdkWorkerThreads() + */ + class DpdkDeviceList + { + friend class KniDeviceList; + private: + bool m_IsInitialized; + static bool m_IsDpdkInitialized; + static uint32_t m_MBufPoolSizePerDevice; + static CoreMask m_CoreMask; + std::vector m_DpdkDeviceList; + std::vector m_WorkerThreads; + + DpdkDeviceList(); + + bool isInitialized() const { return (m_IsInitialized && m_IsDpdkInitialized); } + bool initDpdkDevices(uint32_t mBufPoolSizePerDevice); + static bool verifyHugePagesAndDpdkDriver(); + + static int dpdkWorkerThreadStart(void* ptr); + public: + + ~DpdkDeviceList(); + + /** + * As DpdkDeviceList is a singleton, this is the static getter to retrieve its instance. Note that if the static method + * initDpdk() was not called or returned false this instance won't be initialized and DpdkDevices won't be initialized either + * @return The singleton instance of DpdkDeviceList + */ + static DpdkDeviceList& getInstance() + { + static DpdkDeviceList instance; + if (!instance.isInitialized()) + instance.initDpdkDevices(DpdkDeviceList::m_MBufPoolSizePerDevice); + + return instance; + } + + /** + * A static method that has to be called once at the startup of every application that uses DPDK. It does several things: + * - verifies huge-pages are set and DPDK kernel module is loaded (these are set by the setup_dpdk.py external script that + * has to be run before application is started) + * - initializes the DPDK infrastructure + * - creates DpdkDevice instances for all ports available for DPDK + * + * @param[in] coreMask The cores to initialize DPDK with. After initialization, DPDK will only be able to use these cores + * for its work. The core mask should have a bit set for every core to use. For example: if the user want to use cores 1,2 + * the core mask should be 6 (binary: 110) + * @param[in] mBufPoolSizePerDevice The mbuf pool size each DpdkDevice will have. This has to be a number which is a power of 2 + * minus 1, for example: 1023 (= 2^10-1) or 4,294,967,295 (= 2^32-1), etc. This is a DPDK limitation, not PcapPlusPlus. + * The size of the mbuf pool size dictates how many packets can be handled by the application at the same time. For example: if + * pool size is 1023 it means that no more than 1023 packets can be handled or stored in application memory at every point in time + * @param[in] masterCore The core DPDK will use as master to control all worker thread. The default, unless set otherwise, is 0 + * @param[in] initDpdkArgc Number of optional arguments + * @param[in] initDpdkArgv Optional arguments + * @param[in] appName program name to be provided for the DPDK + * @return True if initialization succeeded or false if huge-pages or DPDK kernel driver are not loaded, if mBufPoolSizePerDevice + * isn't power of 2 minus 1, if DPDK infra initialization failed or if DpdkDevice initialization failed. Anyway, if this method + * returned false it's impossible to use DPDK with PcapPlusPlus. You can get some more details about mbufs and pools in + * DpdkDevice.h file description or in DPDK web site + */ + static bool initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice, uint8_t masterCore = 0, uint32_t initDpdkArgc = 0, char **initDpdkArgv = NULL, const std::string& appName = "pcapplusplusapp"); + + /** + * Get a DpdkDevice by port ID + * @param[in] portId The port ID + * @return A pointer to the DpdkDevice or NULL if no such device is found + */ + DpdkDevice* getDeviceByPort(int portId) const; + + /** + * Get a DpdkDevice by port PCI address + * @param[in] pciAddr The port PCI address + * @return A pointer to the DpdkDevice or NULL if no such device is found + */ + DpdkDevice* getDeviceByPciAddress(const std::string& pciAddr) const; + + /** + * @return A vector of all DpdkDevice instances + */ + const std::vector& getDpdkDeviceList() const { return m_DpdkDeviceList; } + + /** + * @return DPDK master core which is the core that initializes the application + */ + SystemCore getDpdkMasterCore() const; + + /** + * Change the log level of all modules of DPDK + * @param[in] logLevel The log level to set. Logger#Info is RTE_LOG_NOTICE and Logger#Debug is RTE_LOG_DEBUG + */ + void setDpdkLogLevel(Logger::LogLevel logLevel); + + /** + * @return The current DPDK log level. RTE_LOG_NOTICE and lower are considered as Logger#Info. RTE_LOG_INFO or RTE_LOG_DEBUG + * are considered as Logger#Debug + */ + Logger::LogLevel getDpdkLogLevel() const; + + /** + * Order DPDK to write all its logs to a file + * @param[in] logFile The file to write to + * @return True if action succeeded, false otherwise + */ + bool writeDpdkLogToFile(FILE* logFile); + + /** + * There are two ways to capture packets using DpdkDevice: one of them is using worker threads and the other way is setting + * a callback which is invoked each time a burst of packets is captured (see DpdkDevice#startCaptureSingleThread() ). This + * method implements the first way. See a detailed description of workers in DpdkWorkerThread class description. This method + * gets a vector of workers (classes that implement the DpdkWorkerThread interface) and a core mask and starts a worker thread + * on each core (meaning - call the worker's DpdkWorkerThread#run() method). Workers usually run in an endless loop and will + * be ordered to stop by calling stopDpdkWorkerThreads().
+ * Note that number of cores in the core mask must be equal to the number of workers. In addition it's impossible to run a + * worker thread on DPDK master core, so the core mask shouldn't include the master core (you can find the master core by + * calling getDpdkMasterCore() ). + * @param[in] coreMask The bitmask of cores to run worker threads on. This list shouldn't include DPDK master core + * @param[in] workerThreadsVec A vector of worker instances to run (classes who implement the DpdkWorkerThread interface). + * Number of workers in this vector must be equal to the number of cores in the core mask. Notice that the instances of + * DpdkWorkerThread shouldn't be freed until calling stopDpdkWorkerThreads() as these instances are running + * @return True if all worker threads started successfully or false if: DPDK isn't initialized (initDpdk() wasn't called or + * returned false), number of cores differs from number of workers, core mask includes DPDK master core or if one of the + * worker threads couldn't be run + */ + bool startDpdkWorkerThreads(CoreMask coreMask, std::vector& workerThreadsVec); + + /** + * Assuming worker threads are running, this method orders them to stop by calling DpdkWorkerThread#stop(). Then it waits until + * they stop running + */ + void stopDpdkWorkerThreads(); + }; + +} // namespace pcpp + +#endif /* PCAPPP_DPDK_DEVICE_LIST */ diff --git a/Pcap++/header/NetworkUtils.h b/Pcap++/header/NetworkUtils.h index 7b09424d61..edb0cc09ad 100644 --- a/Pcap++/header/NetworkUtils.h +++ b/Pcap++/header/NetworkUtils.h @@ -1,90 +1,90 @@ -#ifndef PCAPPP_NETWORK_UTILS -#define PCAPPP_NETWORK_UTILS - -#include "MacAddress.h" -#include "IpAddress.h" -#include "PcapLiveDevice.h" - - -/// @file - -/** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @class NetworkUtils - * This class bundles several network utilities that are very common and useful. These utilities use Pcap++ and Packet++ packet - * crafting and processing capabilities. This class is a singleton and can be access by getInstance() only - */ - class NetworkUtils - { - public: - - /** - * The access method to the singleton - * @return The singleton instance of this class - */ - static NetworkUtils& getInstance() - { - static NetworkUtils instance; - return instance; - } - - /** - * Default timeout used for several utilities. Currently set to 5 seconds - */ - static const int DefaultTimeout; - - /** - * Resolve the MAC address for a given IPv4 address. It's done using the ARP protocol: send an ARP request and interpret the response - * @param[in] ipAddr The IPv4 address to resolve MAC address to - * @param[in] device The interface to send and receive the ARP packets on - * @param[out] arpResponseTimeMS An output parameter that will contain the time in milliseconds that took the ARP response to arrive - * @param[in] sourceMac An optional parameter to set the source MAC address that will be sent with the ARP request - * if this parameter isn't set or set with MacAddress#Zero the MAC address of the interface will be used - * @param[in] sourceIP An optional parameter to set the source IPv4 address that will be sent with the ARP request - * if this parameter isn't set or set with IPv4Address#Zero the default IPv4 address of the interface will be used - * @param[in] arpTimeout An optional parameter to set the timeout to wait for the ARP response to return. - * If this parameter isn't set or set with a number smaller than 0, a default timeout of 5 seconds will be set - * @return The resolved MAC address or MacAddress#Zero if an error occurred or address could not be resolved. Errors will be printed - * to log - */ - MacAddress getMacAddress(IPv4Address ipAddr, PcapLiveDevice* device, double& arpResponseTimeMS, - MacAddress sourceMac = MacAddress::Zero, IPv4Address sourceIP = IPv4Address::Zero, int arpTimeout = -1) const; - - - /** - * Resolve an IPv4 address for a given hostname. Resolving is done in multiple phases: first resolving the LAN gateway MAC address - * (or default gateway if a gateway isn't provided) using ARP protocol (by using NetworkUtils#getMacAddress() ). Then a DNS request - * is sent to a DNS server (if specified) or to the LAN gateway (if DNS server is not specified). The DNS response is decoded and - * the IPv4 address is determined. In addition the method outputs the time it took the DNS response to arrive and the DNS TTL - * written on the DNS response. If DNS response doesn't contain an IPv4 address resolving an IPv4Address#Zero will be returned. - * @param[in] hostname The hostname to resolve - * @param[in] device The interface to send and receive packets on - * @param[out] dnsResponseTimeMS When method returns successfully will contain the time it took to receive the DNS response - * (in milli-seconds) - * @param[out] dnsTTL When method returns successfully will contain The DNS TTL written in the DNS response - * @param[in] dnsTimeout An optional parameter to specify the timeout to wait for a DNS response. If not specified the default timeout - * is 5 sec - * @param[in] dnsServerIP An optional parameter to specify the DNS server IP to send the DNS request to. If not specified - * or specified with IPv4Address#Zero the DNS request will be sent to the default DNS server configured in the system - * @param[in] gatewayIP An optional parameter to specify the LAN gateway to send the DNS request through. If not specified - * or specified with IPv4Address#Zero the interface's default gateway will be used - * @return The resolved IPv4 address or IPv4Address#Zero if something went wrong (in this case an error will be printed to log) - */ - IPv4Address getIPv4Address(const std::string& hostname, PcapLiveDevice* device, double& dnsResponseTimeMS, uint32_t& dnsTTL, - int dnsTimeout = -1, IPv4Address dnsServerIP = IPv4Address::Zero, IPv4Address gatewayIP = IPv4Address::Zero) const; - - private: - - // private c'tor - NetworkUtils() {} - }; - -} // namespace pcpp - -#endif /* PCAPPP_NETWORK_UTILS */ +#ifndef PCAPPP_NETWORK_UTILS +#define PCAPPP_NETWORK_UTILS + +#include "MacAddress.h" +#include "IpAddress.h" +#include "PcapLiveDevice.h" + + +/// @file + +/** +* \namespace pcpp +* \brief The main namespace for the PcapPlusPlus lib +*/ +namespace pcpp +{ + + /** + * @class NetworkUtils + * This class bundles several network utilities that are very common and useful. These utilities use Pcap++ and Packet++ packet + * crafting and processing capabilities. This class is a singleton and can be access by getInstance() only + */ + class NetworkUtils + { + public: + + /** + * The access method to the singleton + * @return The singleton instance of this class + */ + static NetworkUtils& getInstance() + { + static NetworkUtils instance; + return instance; + } + + /** + * Default timeout used for several utilities. Currently set to 5 seconds + */ + static const int DefaultTimeout; + + /** + * Resolve the MAC address for a given IPv4 address. It's done using the ARP protocol: send an ARP request and interpret the response + * @param[in] ipAddr The IPv4 address to resolve MAC address to + * @param[in] device The interface to send and receive the ARP packets on + * @param[out] arpResponseTimeMS An output parameter that will contain the time in milliseconds that took the ARP response to arrive + * @param[in] sourceMac An optional parameter to set the source MAC address that will be sent with the ARP request + * if this parameter isn't set or set with MacAddress#Zero the MAC address of the interface will be used + * @param[in] sourceIP An optional parameter to set the source IPv4 address that will be sent with the ARP request + * if this parameter isn't set or set with IPv4Address#Zero the default IPv4 address of the interface will be used + * @param[in] arpTimeout An optional parameter to set the timeout to wait for the ARP response to return. + * If this parameter isn't set or set with a number smaller than 0, a default timeout of 5 seconds will be set + * @return The resolved MAC address or MacAddress#Zero if an error occurred or address could not be resolved. Errors will be printed + * to log + */ + MacAddress getMacAddress(IPv4Address ipAddr, PcapLiveDevice* device, double& arpResponseTimeMS, + MacAddress sourceMac = MacAddress::Zero, IPv4Address sourceIP = IPv4Address::Zero, int arpTimeout = -1) const; + + + /** + * Resolve an IPv4 address for a given hostname. Resolving is done in multiple phases: first resolving the LAN gateway MAC address + * (or default gateway if a gateway isn't provided) using ARP protocol (by using NetworkUtils#getMacAddress() ). Then a DNS request + * is sent to a DNS server (if specified) or to the LAN gateway (if DNS server is not specified). The DNS response is decoded and + * the IPv4 address is determined. In addition the method outputs the time it took the DNS response to arrive and the DNS TTL + * written on the DNS response. If DNS response doesn't contain an IPv4 address resolving an IPv4Address#Zero will be returned. + * @param[in] hostname The hostname to resolve + * @param[in] device The interface to send and receive packets on + * @param[out] dnsResponseTimeMS When method returns successfully will contain the time it took to receive the DNS response + * (in milli-seconds) + * @param[out] dnsTTL When method returns successfully will contain The DNS TTL written in the DNS response + * @param[in] dnsTimeout An optional parameter to specify the timeout to wait for a DNS response. If not specified the default timeout + * is 5 sec + * @param[in] dnsServerIP An optional parameter to specify the DNS server IP to send the DNS request to. If not specified + * or specified with IPv4Address#Zero the DNS request will be sent to the default DNS server configured in the system + * @param[in] gatewayIP An optional parameter to specify the LAN gateway to send the DNS request through. If not specified + * or specified with IPv4Address#Zero the interface's default gateway will be used + * @return The resolved IPv4 address or IPv4Address#Zero if something went wrong (in this case an error will be printed to log) + */ + IPv4Address getIPv4Address(const std::string& hostname, PcapLiveDevice* device, double& dnsResponseTimeMS, uint32_t& dnsTTL, + int dnsTimeout = -1, IPv4Address dnsServerIP = IPv4Address::Zero, IPv4Address gatewayIP = IPv4Address::Zero) const; + + private: + + // private c'tor + NetworkUtils() {} + }; + +} // namespace pcpp + +#endif /* PCAPPP_NETWORK_UTILS */ diff --git a/Pcap++/header/PcapDevice.h b/Pcap++/header/PcapDevice.h index ceac96fe4a..555c4ff5bd 100644 --- a/Pcap++/header/PcapDevice.h +++ b/Pcap++/header/PcapDevice.h @@ -1,100 +1,100 @@ -#ifndef PCAPPP_PCAP_DEVICE -#define PCAPPP_PCAP_DEVICE - -#include "Device.h" - -// forward declaration for the pcap descriptor defined in pcap.h -struct pcap; -typedef pcap pcap_t; -struct pcap_pkthdr; - -/// @file - -/** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - //Forward Declaration - required for IPcapDevice::matchPacketWithFilter - class GeneralFilter; - - /** - * @class IPcapDevice - * An abstract class representing all libpcap-based packet capturing devices: files, libPcap, WinPcap/Npcap and RemoteCapture. - * This class is abstract and cannot be instantiated - */ - class IPcapDevice : public IDevice, public IFilterableDevice - { - protected: - pcap_t* m_PcapDescriptor; - - // c'tor should not be public - IPcapDevice() : IDevice() { m_PcapDescriptor = NULL; } - - public: - - /** - * @struct PcapStats - * A container for pcap device statistics - */ - struct PcapStats - { - /** Number of packets received */ - uint64_t packetsRecv; - /** Number of packets dropped */ - uint64_t packetsDrop; - /** number of packets dropped by interface (not supported on all platforms) */ - uint64_t packetsDropByInterface; - }; - - - virtual ~IPcapDevice(); - - /** - * Get statistics from the device - * @param[out] stats An object containing the stats - * @return No return value - */ - virtual void getStatistics(PcapStats& stats) const = 0; - - /** - * A static method for retrieving pcap lib (libpcap/WinPcap/etc.) version information. This method is actually - * a wrapper for [pcap_lib_version()](https://www.tcpdump.org/manpages/pcap_lib_version.3pcap.html) - * @return A string containing the pcap lib version information - */ - static std::string getPcapLibVersionInfo(); - - /** - * Match a raw packet with a given BPF filter. Notice this method is static which means you don't need any device instance - * in order to perform this match - * @param[in] filter A filter class to test against - * @param[in] rawPacket A pointer to the raw packet to match the filter with - * @return True if raw packet matches the filter or false otherwise - */ - static bool matchPacketWithFilter(GeneralFilter& filter, RawPacket* rawPacket); - - - // implement abstract methods - - using IFilterableDevice::setFilter; - - /** - * Set a filter for the device. When implemented by the device, only packets that match the filter will be received. - * Please note that when the device is closed the filter is reset so when reopening the device you need to call this - * method again in order to reactivate the filter - * @param[in] filterAsString The filter to be set in Berkeley Packet Filter (BPF) syntax (http://biot.com/capstats/bpf.html) - * @return True if filter set successfully, false otherwise - */ - virtual bool setFilter(std::string filterAsString); - - /** - * Clear the filter currently set on device - * @return True if filter was removed successfully or if no filter was set, false otherwise - */ - bool clearFilter(); - }; - -} // namespace pcpp - -#endif // PCAPPP_PCAP_DEVICE +#ifndef PCAPPP_PCAP_DEVICE +#define PCAPPP_PCAP_DEVICE + +#include "Device.h" + +// forward declaration for the pcap descriptor defined in pcap.h +struct pcap; +typedef pcap pcap_t; +struct pcap_pkthdr; + +/// @file + +/** +* \namespace pcpp +* \brief The main namespace for the PcapPlusPlus lib +*/ +namespace pcpp +{ + //Forward Declaration - required for IPcapDevice::matchPacketWithFilter + class GeneralFilter; + + /** + * @class IPcapDevice + * An abstract class representing all libpcap-based packet capturing devices: files, libPcap, WinPcap/Npcap and RemoteCapture. + * This class is abstract and cannot be instantiated + */ + class IPcapDevice : public IDevice, public IFilterableDevice + { + protected: + pcap_t* m_PcapDescriptor; + + // c'tor should not be public + IPcapDevice() : IDevice() { m_PcapDescriptor = NULL; } + + public: + + /** + * @struct PcapStats + * A container for pcap device statistics + */ + struct PcapStats + { + /** Number of packets received */ + uint64_t packetsRecv; + /** Number of packets dropped */ + uint64_t packetsDrop; + /** number of packets dropped by interface (not supported on all platforms) */ + uint64_t packetsDropByInterface; + }; + + + virtual ~IPcapDevice(); + + /** + * Get statistics from the device + * @param[out] stats An object containing the stats + * @return No return value + */ + virtual void getStatistics(PcapStats& stats) const = 0; + + /** + * A static method for retrieving pcap lib (libpcap/WinPcap/etc.) version information. This method is actually + * a wrapper for [pcap_lib_version()](https://www.tcpdump.org/manpages/pcap_lib_version.3pcap.html) + * @return A string containing the pcap lib version information + */ + static std::string getPcapLibVersionInfo(); + + /** + * Match a raw packet with a given BPF filter. Notice this method is static which means you don't need any device instance + * in order to perform this match + * @param[in] filter A filter class to test against + * @param[in] rawPacket A pointer to the raw packet to match the filter with + * @return True if raw packet matches the filter or false otherwise + */ + static bool matchPacketWithFilter(GeneralFilter& filter, RawPacket* rawPacket); + + + // implement abstract methods + + using IFilterableDevice::setFilter; + + /** + * Set a filter for the device. When implemented by the device, only packets that match the filter will be received. + * Please note that when the device is closed the filter is reset so when reopening the device you need to call this + * method again in order to reactivate the filter + * @param[in] filterAsString The filter to be set in Berkeley Packet Filter (BPF) syntax (http://biot.com/capstats/bpf.html) + * @return True if filter set successfully, false otherwise + */ + virtual bool setFilter(std::string filterAsString); + + /** + * Clear the filter currently set on device + * @return True if filter was removed successfully or if no filter was set, false otherwise + */ + bool clearFilter(); + }; + +} // namespace pcpp + +#endif // PCAPPP_PCAP_DEVICE diff --git a/Pcap++/header/PcapFileDevice.h b/Pcap++/header/PcapFileDevice.h index 182885e00b..f39e81b82f 100644 --- a/Pcap++/header/PcapFileDevice.h +++ b/Pcap++/header/PcapFileDevice.h @@ -1,595 +1,595 @@ -#ifndef PCAPPP_FILE_DEVICE -#define PCAPPP_FILE_DEVICE - -#include "PcapDevice.h" -#include "RawPacket.h" -#include - -// forward declaration for structs and typedefs defined in pcap.h -struct pcap_dumper; -typedef struct pcap_dumper pcap_dumper_t; - -/// @file - -/** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @class IFileDevice - * An abstract class (cannot be instantiated, has a private c'tor) which is the parent class for all file devices - */ - class IFileDevice : public IPcapDevice - { - protected: - std::string m_FileName; - - explicit IFileDevice(const std::string& fileName); - virtual ~IFileDevice(); - - public: - - /** - * @return The name of the file - */ - std::string getFileName() const; - - - //override methods - - /** - * Close the file - * @return No return value - */ - virtual void close(); - }; - - - /** - * @class IFileReaderDevice - * An abstract class (cannot be instantiated, has a private c'tor) which is the parent class for file reader devices - */ - class IFileReaderDevice : public IFileDevice - { - protected: - uint32_t m_NumOfPacketsRead; - uint32_t m_NumOfPacketsNotParsed; - - /** - * A constructor for this class that gets the pcap full path file name to open. Notice that after calling this constructor the file - * isn't opened yet, so reading packets will fail. For opening the file call open() - * @param[in] fileName The full path of the file to read - */ - IFileReaderDevice(const std::string& fileName); - - public: - - /** - * A destructor for this class - */ - virtual ~IFileReaderDevice() {} - - /** - * @return The file size in bytes - */ - uint64_t getFileSize() const; - - virtual bool getNextPacket(RawPacket& rawPacket) = 0; - - /** - * Read the next N packets into a raw packet vector - * @param[out] packetVec The raw packet vector to read packets into - * @param[in] numOfPacketsToRead Number of packets to read. If value <0 all remaining packets in the file will be read into the - * raw packet vector (this is the default value) - * @return The number of packets actually read - */ - int getNextPackets(RawPacketVector& packetVec, int numOfPacketsToRead = -1); - - /** - * A static method that creates an instance of the reader best fit to read the file. It decides by the file extension: for .pcapng - * files it returns an instance of PcapNgFileReaderDevice and for all other extensions it returns an instance of PcapFileReaderDevice - * @param[in] fileName The file name to open - * @return An instance of the reader to read the file. Notice you should free this instance when done using it - */ - static IFileReaderDevice* getReader(const std::string& fileName); - }; - - - /** - * @class PcapFileReaderDevice - * A class for opening a pcap file in read-only mode. This class enable to open the file and read all packets, packet-by-packet - */ - class PcapFileReaderDevice : public IFileReaderDevice - { - private: - LinkLayerType m_PcapLinkLayerType; - - // private copy c'tor - PcapFileReaderDevice(const PcapFileReaderDevice& other); - PcapFileReaderDevice& operator=(const PcapFileReaderDevice& other); - - public: - /** - * A constructor for this class that gets the pcap full path file name to open. Notice that after calling this constructor the file - * isn't opened yet, so reading packets will fail. For opening the file call open() - * @param[in] fileName The full path of the file to read - */ - PcapFileReaderDevice(const std::string& fileName) : IFileReaderDevice(fileName), m_PcapLinkLayerType(LINKTYPE_ETHERNET) {} - - /** - * A destructor for this class - */ - virtual ~PcapFileReaderDevice() {} - - /** - * @return The link layer type of this file - */ - LinkLayerType getLinkLayerType() const { return m_PcapLinkLayerType; } - - - //overridden methods - - /** - * Read the next packet from the file. Before using this method please verify the file is opened using open() - * @param[out] rawPacket A reference for an empty RawPacket where the packet will be written - * @return True if a packet was read successfully. False will be returned if the file isn't opened (also, an error log will be printed) - * or if reached end-of-file - */ - bool getNextPacket(RawPacket& rawPacket); - - /** - * Open the file name which path was specified in the constructor in a read-only mode - * @return True if file was opened successfully or if file is already opened. False if opening the file failed for some reason (for example: - * file path does not exist) - */ - bool open(); - - /** - * Get statistics of packets read so far. In the PcapStats struct, only the packetsRecv member is relevant. The rest of the members will contain 0 - * @param[out] stats The stats struct where stats are returned - */ - void getStatistics(PcapStats& stats) const; - }; - - /** - * @class SnoopFileReaderDevice - * A class for opening a snoop file in read-only mode. This class enable to open the file and read all packets, packet-by-packet - */ - class SnoopFileReaderDevice : public IFileReaderDevice - { - private: - #pragma pack(1) - /* - * File format header. - */ - typedef struct { - uint64_t identification_pattern; - uint32_t version_number; - uint32_t datalink_type; - } snoop_file_header_t; - - /* - * Packet record header. - */ - typedef struct { - uint32_t original_length; /* original packet length */ - uint32_t included_length; /* saved packet length */ - uint32_t packet_record_length;/* total record length */ - uint32_t ndrops_cumulative; /* cumulative drops */ - uint32_t time_sec; /* timestamp */ - uint32_t time_usec; /* microsecond timestamp */ - } snoop_packet_header_t; - #pragma pack() - - LinkLayerType m_PcapLinkLayerType; - std::ifstream m_snoopFile; - - // private copy c'tor - SnoopFileReaderDevice(const PcapFileReaderDevice& other); - SnoopFileReaderDevice& operator=(const PcapFileReaderDevice& other); - - public: - /** - * A constructor for this class that gets the snoop full path file name to open. Notice that after calling this constructor the file - * isn't opened yet, so reading packets will fail. For opening the file call open() - * @param[in] fileName The full path of the file to read - */ - SnoopFileReaderDevice(const std::string& fileName) : IFileReaderDevice(fileName), m_PcapLinkLayerType(LINKTYPE_ETHERNET) {} - - /** - * A destructor for this class - */ - virtual ~SnoopFileReaderDevice(); - - /** - * @return The link layer type of this file - */ - LinkLayerType getLinkLayerType() const { return m_PcapLinkLayerType; } - - - //overridden methods - - /** - * Read the next packet from the file. Before using this method please verify the file is opened using open() - * @param[out] rawPacket A reference for an empty RawPacket where the packet will be written - * @return True if a packet was read successfully. False will be returned if the file isn't opened (also, an error log will be printed) - * or if reached end-of-file - */ - bool getNextPacket(RawPacket& rawPacket); - - /** - * Open the file name which path was specified in the constructor in a read-only mode - * @return True if file was opened successfully or if file is already opened. False if opening the file failed for some reason (for example: - * file path does not exist) - */ - bool open(); - - /** - * Get statistics of packets read so far. In the PcapStats struct, only the packetsRecv member is relevant. The rest of the members will contain 0 - * @param[out] stats The stats struct where stats are returned - */ - void getStatistics(PcapStats& stats) const; - - /** - * Close the snoop file - */ - void close(); - }; - - - /** - * @class PcapNgFileReaderDevice - * A class for opening a pcap-ng file in read-only mode. This class enable to open the file and read all packets, packet-by-packet - */ - class PcapNgFileReaderDevice : public IFileReaderDevice - { - private: - void* m_LightPcapNg; - BpfFilterWrapper m_BpfWrapper; - - // private copy c'tor - PcapNgFileReaderDevice(const PcapNgFileReaderDevice& other); - PcapNgFileReaderDevice& operator=(const PcapNgFileReaderDevice& other); - - public: - /** - * A constructor for this class that gets the pcap-ng full path file name to open. Notice that after calling this constructor the file - * isn't opened yet, so reading packets will fail. For opening the file call open() - * @param[in] fileName The full path of the file to read - */ - PcapNgFileReaderDevice(const std::string& fileName); - - /** - * A destructor for this class - */ - virtual ~PcapNgFileReaderDevice() { close(); } - - /** - * The pcap-ng format allows storing metadata at the header of the file. Part of this metadata is a string specifying the - * operating system that was used for capturing the packets. This method reads this string from the metadata (if exists) and - * returns it - * @return The operating system string if exists, or an empty string otherwise - */ - std::string getOS() const; - - /** - * The pcap-ng format allows storing metadata at the header of the file. Part of this metadata is a string specifying the - * hardware that was used for capturing the packets. This method reads this string from the metadata (if exists) and - * returns it - * @return The hardware string if exists, or an empty string otherwise - */ - std::string getHardware() const; - - /** - * The pcap-ng format allows storing metadata at the header of the file. Part of this metadata is a string specifying the - * capture application that was used for capturing the packets. This method reads this string from the metadata (if exists) and - * returns it - * @return The capture application string if exists, or an empty string otherwise - */ - std::string getCaptureApplication() const; - - /** - * The pcap-ng format allows storing metadata at the header of the file. Part of this metadata is a string containing a user-defined - * comment (can be any string). This method reads this string from the metadata (if exists) and - * returns it - * @return The comment written inside the file if exists, or an empty string otherwise - */ - std::string getCaptureFileComment() const; - - /** - * The pcap-ng format allows storing a user-defined comment for every packet (besides the comment per-file). This method reads - * the next packet and the comment attached to it (if such comment exists), and returns them both - * @param[out] rawPacket A reference for an empty RawPacket where the packet will be written - * @param[out] packetComment The comment attached to the packet or an empty string if no comment exists - * @return True if a packet was read successfully. False will be returned if the file isn't opened (also, an error log will be printed) - * or if reached end-of-file - */ - bool getNextPacket(RawPacket& rawPacket, std::string& packetComment); - - //overridden methods - - /** - * Read the next packet from the file. Before using this method please verify the file is opened using open() - * @param[out] rawPacket A reference for an empty RawPacket where the packet will be written - * @return True if a packet was read successfully. False will be returned if the file isn't opened (also, an error log will be printed) - * or if reached end-of-file - */ - bool getNextPacket(RawPacket& rawPacket); - - /** - * Open the file name which path was specified in the constructor in a read-only mode - * @return True if file was opened successfully or if file is already opened. False if opening the file failed for some reason (for example: - * file path does not exist) - */ - bool open(); - - /** - * Get statistics of packets read so far. - * @param[out] stats The stats struct where stats are returned - */ - void getStatistics(PcapStats& stats) const; - - /** - * Set a filter for PcapNG reader device. Only packets that match the filter will be received - * @param[in] filterAsString The filter to be set in Berkeley Packet Filter (BPF) syntax (http://biot.com/capstats/bpf.html) - * @return True if filter set successfully, false otherwise - */ - bool setFilter(std::string filterAsString); - - /** - * Close the pacp-ng file - */ - void close(); - }; - - - /** - * @class IFileWriterDevice - * An abstract class (cannot be instantiated, has a private c'tor) which is the parent class for file writer devices - */ - class IFileWriterDevice : public IFileDevice - { - protected: - uint32_t m_NumOfPacketsWritten; - uint32_t m_NumOfPacketsNotWritten; - - IFileWriterDevice(const std::string& fileName); - - public: - - /** - * A destructor for this class - */ - virtual ~IFileWriterDevice() {} - - virtual bool writePacket(RawPacket const& packet) = 0; - - virtual bool writePackets(const RawPacketVector& packets) = 0; - - using IFileDevice::open; - virtual bool open(bool appendMode) = 0; - }; - - - /** - * @class PcapFileWriterDevice - * A class for opening a pcap file for writing or create a new pcap file and write packets to it. This class adds - * a unique capability that isn't supported in WinPcap and in older libpcap versions which is to open a pcap file - * in append mode where packets are written at the end of the pcap file instead of running it over - */ - class PcapFileWriterDevice : public IFileWriterDevice - { - private: - pcap_dumper_t* m_PcapDumpHandler; - LinkLayerType m_PcapLinkLayerType; - bool m_AppendMode; - FILE* m_File; - - // private copy c'tor - PcapFileWriterDevice(const PcapFileWriterDevice& other); - PcapFileWriterDevice& operator=(const PcapFileWriterDevice& other); - - void closeFile(); - - public: - /** - * A constructor for this class that gets the pcap full path file name to open for writing or create. Notice that after calling this - * constructor the file isn't opened yet, so writing packets will fail. For opening the file call open() - * @param[in] fileName The full path of the file - * @param[in] linkLayerType The link layer type all packet in this file will be based on. The default is Ethernet - */ - PcapFileWriterDevice(const std::string& fileName, LinkLayerType linkLayerType = LINKTYPE_ETHERNET); - - /** - * A destructor for this class - */ - ~PcapFileWriterDevice() {} - - /** - * Write a RawPacket to the file. Before using this method please verify the file is opened using open(). This method won't change the - * written packet - * @param[in] packet A reference for an existing RawPcket to write to the file - * @return True if a packet was written successfully. False will be returned if the file isn't opened - * or if the packet link layer type is different than the one defined for the file - * (in all cases, an error will be printed to log) - */ - bool writePacket(RawPacket const& packet); - - /** - * Write multiple RawPacket to the file. Before using this method please verify the file is opened using open(). This method won't change - * the written packets or the RawPacketVector instance - * @param[in] packets A reference for an existing RawPcketVector, all of its packets will be written to the file - * @return True if all packets were written successfully to the file. False will be returned if the file isn't opened (also, an error - * log will be printed) or if at least one of the packets wasn't written successfully to the file - */ - bool writePackets(const RawPacketVector& packets); - - //override methods - - /** - * Open the file in a write mode. If file doesn't exist, it will be created. If it does exist it will be - * overwritten, meaning all its current content will be deleted - * @return True if file was opened/created successfully or if file is already opened. False if opening the file failed for some reason - * (an error will be printed to log) - */ - virtual bool open(); - - /** - * Same as open(), but enables to open the file in append mode in which packets will be appended to the file - * instead of overwrite its current content. In append mode file must exist, otherwise opening will fail - * @param[in] appendMode A boolean indicating whether to open the file in append mode or not. If set to false - * this method will act exactly like open(). If set to true, file will be opened in append mode - * @return True of managed to open the file successfully. In case appendMode is set to true, false will be returned - * if file wasn't found or couldn't be read, if file type is not pcap, or if link type specified in c'tor is - * different from current file link type. In case appendMode is set to false, please refer to open() for return - * values - */ - bool open(bool appendMode); - - /** - * Flush and close the pacp file - */ - virtual void close(); - - /** - * Flush packets to disk. - */ - void flush(); - - /** - * Get statistics of packets written so far. - * @param[out] stats The stats struct where stats are returned - */ - virtual void getStatistics(PcapStats& stats) const; - }; - - - /** - * @class PcapNgFileWriterDevice - * A class for opening a pcap-ng file for writing or creating a new pcap-ng file and write packets to it. This class adds - * unique capabilities such as writing metadata attributes into the file header, adding comments per packet and opening - * the file in append mode where packets are added to a file instead of overriding it. This capabilities are part of the - * pcap-ng standard but aren't supported in most tools and libraries - */ - class PcapNgFileWriterDevice : public IFileWriterDevice - { - private: - void* m_LightPcapNg; - int m_CompressionLevel; - BpfFilterWrapper m_BpfWrapper; - - // private copy c'tor - PcapNgFileWriterDevice(const PcapFileWriterDevice& other); - PcapNgFileWriterDevice& operator=(const PcapNgFileWriterDevice& other); - - public: - - /** - * A constructor for this class that gets the pcap-ng full path file name to open for writing or create. Notice that after calling this - * constructor the file isn't opened yet, so writing packets will fail. For opening the file call open() - * @param[in] fileName The full path of the file - * @param[in] compressionLevel The compression level to use when writing the file, use 0 to disable compression or 10 for max compression. Default is 0 - */ - PcapNgFileWriterDevice(const std::string& fileName, int compressionLevel = 0); - - /** - * A destructor for this class - */ - virtual ~PcapNgFileWriterDevice() { close(); } - - /** - * Open the file in a write mode. If file doesn't exist, it will be created. If it does exist it will be - * overwritten, meaning all its current content will be deleted. As opposed to open(), this method also allows writing several - * metadata attributes that will be stored in the header of the file - * @param[in] os A string describing the operating system that was used to capture the packets. If this string is empty or null it - * will be ignored - * @param[in] hardware A string describing the hardware that was used to capture the packets. If this string is empty or null it - * will be ignored - * @param[in] captureApp A string describing the application that was used to capture the packets. If this string is empty or null it - * will be ignored - * @param[in] fileComment A string containing a user-defined comment that will be part of the metadata of the file. - * If this string is empty or null it will be ignored - * @return True if file was opened/created successfully or if file is already opened. False if opening the file failed for some reason - * (an error will be printed to log) - */ - bool open(const std::string& os, const std::string& hardware, const std::string& captureApp, const std::string& fileComment); - - /** - * The pcap-ng format allows adding a user-defined comment for each stored packet. This method writes a RawPacket to the file and - * adds a comment to it. Before using this method please verify the file is opened using open(). This method won't change the - * written packet or the input comment - * @param[in] packet A reference for an existing RawPcket to write to the file - * @param[in] comment The comment to be written for the packet. If this string is empty or null it will be ignored - * @return True if a packet was written successfully. False will be returned if the file isn't opened (an error will be printed to log) - */ - bool writePacket(RawPacket const& packet, const std::string& comment); - - //overridden methods - - /** - * Write a RawPacket to the file. Before using this method please verify the file is opened using open(). This method won't change the - * written packet - * @param[in] packet A reference for an existing RawPcket to write to the file - * @return True if a packet was written successfully. False will be returned if the file isn't opened (an error will be printed to log) - */ - bool writePacket(RawPacket const& packet); - - /** - * Write multiple RawPacket to the file. Before using this method please verify the file is opened using open(). This method won't change - * the written packets or the RawPacketVector instance - * @param[in] packets A reference for an existing RawPcketVector, all of its packets will be written to the file - * @return True if all packets were written successfully to the file. False will be returned if the file isn't opened (also, an error - * log will be printed) or if at least one of the packets wasn't written successfully to the file - */ - bool writePackets(const RawPacketVector& packets); - - /** - * Open the file in a write mode. If file doesn't exist, it will be created. If it does exist it will be - * overwritten, meaning all its current content will be deleted - * @return True if file was opened/created successfully or if file is already opened. False if opening the file failed for some reason - * (an error will be printed to log) - */ - bool open(); - - /** - * Same as open(), but enables to open the file in append mode in which packets will be appended to the file - * instead of overwrite its current content. In append mode file must exist, otherwise opening will fail - * @param[in] appendMode A boolean indicating whether to open the file in append mode or not. If set to false - * this method will act exactly like open(). If set to true, file will be opened in append mode - * @return True of managed to open the file successfully. In case appendMode is set to true, false will be returned - * if file wasn't found or couldn't be read, if file type is not pcap-ng. In case appendMode is set to false, please refer to open() - * for return values - */ - bool open(bool appendMode); - - /** - * Flush packets to the pcap-ng file - */ - void flush(); - - /** - * Flush and close the pcap-ng file - */ - void close(); - - /** - * Get statistics of packets written so far. - * @param[out] stats The stats struct where stats are returned - */ - void getStatistics(PcapStats& stats) const; - - /** - * Set a filter for PcapNG writer device. Only packets that match the filter will be persisted - * @param[in] filterAsString The filter to be set in Berkeley Packet Filter (BPF) syntax (http://biot.com/capstats/bpf.html) - * @return True if filter set successfully, false otherwise - */ - bool setFilter(std::string filterAsString); - - }; - -}// namespace pcpp - -#endif +#ifndef PCAPPP_FILE_DEVICE +#define PCAPPP_FILE_DEVICE + +#include "PcapDevice.h" +#include "RawPacket.h" +#include + +// forward declaration for structs and typedefs defined in pcap.h +struct pcap_dumper; +typedef struct pcap_dumper pcap_dumper_t; + +/// @file + +/** +* \namespace pcpp +* \brief The main namespace for the PcapPlusPlus lib +*/ +namespace pcpp +{ + + /** + * @class IFileDevice + * An abstract class (cannot be instantiated, has a private c'tor) which is the parent class for all file devices + */ + class IFileDevice : public IPcapDevice + { + protected: + std::string m_FileName; + + explicit IFileDevice(const std::string& fileName); + virtual ~IFileDevice(); + + public: + + /** + * @return The name of the file + */ + std::string getFileName() const; + + + //override methods + + /** + * Close the file + * @return No return value + */ + virtual void close(); + }; + + + /** + * @class IFileReaderDevice + * An abstract class (cannot be instantiated, has a private c'tor) which is the parent class for file reader devices + */ + class IFileReaderDevice : public IFileDevice + { + protected: + uint32_t m_NumOfPacketsRead; + uint32_t m_NumOfPacketsNotParsed; + + /** + * A constructor for this class that gets the pcap full path file name to open. Notice that after calling this constructor the file + * isn't opened yet, so reading packets will fail. For opening the file call open() + * @param[in] fileName The full path of the file to read + */ + IFileReaderDevice(const std::string& fileName); + + public: + + /** + * A destructor for this class + */ + virtual ~IFileReaderDevice() {} + + /** + * @return The file size in bytes + */ + uint64_t getFileSize() const; + + virtual bool getNextPacket(RawPacket& rawPacket) = 0; + + /** + * Read the next N packets into a raw packet vector + * @param[out] packetVec The raw packet vector to read packets into + * @param[in] numOfPacketsToRead Number of packets to read. If value <0 all remaining packets in the file will be read into the + * raw packet vector (this is the default value) + * @return The number of packets actually read + */ + int getNextPackets(RawPacketVector& packetVec, int numOfPacketsToRead = -1); + + /** + * A static method that creates an instance of the reader best fit to read the file. It decides by the file extension: for .pcapng + * files it returns an instance of PcapNgFileReaderDevice and for all other extensions it returns an instance of PcapFileReaderDevice + * @param[in] fileName The file name to open + * @return An instance of the reader to read the file. Notice you should free this instance when done using it + */ + static IFileReaderDevice* getReader(const std::string& fileName); + }; + + + /** + * @class PcapFileReaderDevice + * A class for opening a pcap file in read-only mode. This class enable to open the file and read all packets, packet-by-packet + */ + class PcapFileReaderDevice : public IFileReaderDevice + { + private: + LinkLayerType m_PcapLinkLayerType; + + // private copy c'tor + PcapFileReaderDevice(const PcapFileReaderDevice& other); + PcapFileReaderDevice& operator=(const PcapFileReaderDevice& other); + + public: + /** + * A constructor for this class that gets the pcap full path file name to open. Notice that after calling this constructor the file + * isn't opened yet, so reading packets will fail. For opening the file call open() + * @param[in] fileName The full path of the file to read + */ + PcapFileReaderDevice(const std::string& fileName) : IFileReaderDevice(fileName), m_PcapLinkLayerType(LINKTYPE_ETHERNET) {} + + /** + * A destructor for this class + */ + virtual ~PcapFileReaderDevice() {} + + /** + * @return The link layer type of this file + */ + LinkLayerType getLinkLayerType() const { return m_PcapLinkLayerType; } + + + //overridden methods + + /** + * Read the next packet from the file. Before using this method please verify the file is opened using open() + * @param[out] rawPacket A reference for an empty RawPacket where the packet will be written + * @return True if a packet was read successfully. False will be returned if the file isn't opened (also, an error log will be printed) + * or if reached end-of-file + */ + bool getNextPacket(RawPacket& rawPacket); + + /** + * Open the file name which path was specified in the constructor in a read-only mode + * @return True if file was opened successfully or if file is already opened. False if opening the file failed for some reason (for example: + * file path does not exist) + */ + bool open(); + + /** + * Get statistics of packets read so far. In the PcapStats struct, only the packetsRecv member is relevant. The rest of the members will contain 0 + * @param[out] stats The stats struct where stats are returned + */ + void getStatistics(PcapStats& stats) const; + }; + + /** + * @class SnoopFileReaderDevice + * A class for opening a snoop file in read-only mode. This class enable to open the file and read all packets, packet-by-packet + */ + class SnoopFileReaderDevice : public IFileReaderDevice + { + private: + #pragma pack(1) + /* + * File format header. + */ + typedef struct { + uint64_t identification_pattern; + uint32_t version_number; + uint32_t datalink_type; + } snoop_file_header_t; + + /* + * Packet record header. + */ + typedef struct { + uint32_t original_length; /* original packet length */ + uint32_t included_length; /* saved packet length */ + uint32_t packet_record_length;/* total record length */ + uint32_t ndrops_cumulative; /* cumulative drops */ + uint32_t time_sec; /* timestamp */ + uint32_t time_usec; /* microsecond timestamp */ + } snoop_packet_header_t; + #pragma pack() + + LinkLayerType m_PcapLinkLayerType; + std::ifstream m_snoopFile; + + // private copy c'tor + SnoopFileReaderDevice(const PcapFileReaderDevice& other); + SnoopFileReaderDevice& operator=(const PcapFileReaderDevice& other); + + public: + /** + * A constructor for this class that gets the snoop full path file name to open. Notice that after calling this constructor the file + * isn't opened yet, so reading packets will fail. For opening the file call open() + * @param[in] fileName The full path of the file to read + */ + SnoopFileReaderDevice(const std::string& fileName) : IFileReaderDevice(fileName), m_PcapLinkLayerType(LINKTYPE_ETHERNET) {} + + /** + * A destructor for this class + */ + virtual ~SnoopFileReaderDevice(); + + /** + * @return The link layer type of this file + */ + LinkLayerType getLinkLayerType() const { return m_PcapLinkLayerType; } + + + //overridden methods + + /** + * Read the next packet from the file. Before using this method please verify the file is opened using open() + * @param[out] rawPacket A reference for an empty RawPacket where the packet will be written + * @return True if a packet was read successfully. False will be returned if the file isn't opened (also, an error log will be printed) + * or if reached end-of-file + */ + bool getNextPacket(RawPacket& rawPacket); + + /** + * Open the file name which path was specified in the constructor in a read-only mode + * @return True if file was opened successfully or if file is already opened. False if opening the file failed for some reason (for example: + * file path does not exist) + */ + bool open(); + + /** + * Get statistics of packets read so far. In the PcapStats struct, only the packetsRecv member is relevant. The rest of the members will contain 0 + * @param[out] stats The stats struct where stats are returned + */ + void getStatistics(PcapStats& stats) const; + + /** + * Close the snoop file + */ + void close(); + }; + + + /** + * @class PcapNgFileReaderDevice + * A class for opening a pcap-ng file in read-only mode. This class enable to open the file and read all packets, packet-by-packet + */ + class PcapNgFileReaderDevice : public IFileReaderDevice + { + private: + void* m_LightPcapNg; + BpfFilterWrapper m_BpfWrapper; + + // private copy c'tor + PcapNgFileReaderDevice(const PcapNgFileReaderDevice& other); + PcapNgFileReaderDevice& operator=(const PcapNgFileReaderDevice& other); + + public: + /** + * A constructor for this class that gets the pcap-ng full path file name to open. Notice that after calling this constructor the file + * isn't opened yet, so reading packets will fail. For opening the file call open() + * @param[in] fileName The full path of the file to read + */ + PcapNgFileReaderDevice(const std::string& fileName); + + /** + * A destructor for this class + */ + virtual ~PcapNgFileReaderDevice() { close(); } + + /** + * The pcap-ng format allows storing metadata at the header of the file. Part of this metadata is a string specifying the + * operating system that was used for capturing the packets. This method reads this string from the metadata (if exists) and + * returns it + * @return The operating system string if exists, or an empty string otherwise + */ + std::string getOS() const; + + /** + * The pcap-ng format allows storing metadata at the header of the file. Part of this metadata is a string specifying the + * hardware that was used for capturing the packets. This method reads this string from the metadata (if exists) and + * returns it + * @return The hardware string if exists, or an empty string otherwise + */ + std::string getHardware() const; + + /** + * The pcap-ng format allows storing metadata at the header of the file. Part of this metadata is a string specifying the + * capture application that was used for capturing the packets. This method reads this string from the metadata (if exists) and + * returns it + * @return The capture application string if exists, or an empty string otherwise + */ + std::string getCaptureApplication() const; + + /** + * The pcap-ng format allows storing metadata at the header of the file. Part of this metadata is a string containing a user-defined + * comment (can be any string). This method reads this string from the metadata (if exists) and + * returns it + * @return The comment written inside the file if exists, or an empty string otherwise + */ + std::string getCaptureFileComment() const; + + /** + * The pcap-ng format allows storing a user-defined comment for every packet (besides the comment per-file). This method reads + * the next packet and the comment attached to it (if such comment exists), and returns them both + * @param[out] rawPacket A reference for an empty RawPacket where the packet will be written + * @param[out] packetComment The comment attached to the packet or an empty string if no comment exists + * @return True if a packet was read successfully. False will be returned if the file isn't opened (also, an error log will be printed) + * or if reached end-of-file + */ + bool getNextPacket(RawPacket& rawPacket, std::string& packetComment); + + //overridden methods + + /** + * Read the next packet from the file. Before using this method please verify the file is opened using open() + * @param[out] rawPacket A reference for an empty RawPacket where the packet will be written + * @return True if a packet was read successfully. False will be returned if the file isn't opened (also, an error log will be printed) + * or if reached end-of-file + */ + bool getNextPacket(RawPacket& rawPacket); + + /** + * Open the file name which path was specified in the constructor in a read-only mode + * @return True if file was opened successfully or if file is already opened. False if opening the file failed for some reason (for example: + * file path does not exist) + */ + bool open(); + + /** + * Get statistics of packets read so far. + * @param[out] stats The stats struct where stats are returned + */ + void getStatistics(PcapStats& stats) const; + + /** + * Set a filter for PcapNG reader device. Only packets that match the filter will be received + * @param[in] filterAsString The filter to be set in Berkeley Packet Filter (BPF) syntax (http://biot.com/capstats/bpf.html) + * @return True if filter set successfully, false otherwise + */ + bool setFilter(std::string filterAsString); + + /** + * Close the pacp-ng file + */ + void close(); + }; + + + /** + * @class IFileWriterDevice + * An abstract class (cannot be instantiated, has a private c'tor) which is the parent class for file writer devices + */ + class IFileWriterDevice : public IFileDevice + { + protected: + uint32_t m_NumOfPacketsWritten; + uint32_t m_NumOfPacketsNotWritten; + + IFileWriterDevice(const std::string& fileName); + + public: + + /** + * A destructor for this class + */ + virtual ~IFileWriterDevice() {} + + virtual bool writePacket(RawPacket const& packet) = 0; + + virtual bool writePackets(const RawPacketVector& packets) = 0; + + using IFileDevice::open; + virtual bool open(bool appendMode) = 0; + }; + + + /** + * @class PcapFileWriterDevice + * A class for opening a pcap file for writing or create a new pcap file and write packets to it. This class adds + * a unique capability that isn't supported in WinPcap and in older libpcap versions which is to open a pcap file + * in append mode where packets are written at the end of the pcap file instead of running it over + */ + class PcapFileWriterDevice : public IFileWriterDevice + { + private: + pcap_dumper_t* m_PcapDumpHandler; + LinkLayerType m_PcapLinkLayerType; + bool m_AppendMode; + FILE* m_File; + + // private copy c'tor + PcapFileWriterDevice(const PcapFileWriterDevice& other); + PcapFileWriterDevice& operator=(const PcapFileWriterDevice& other); + + void closeFile(); + + public: + /** + * A constructor for this class that gets the pcap full path file name to open for writing or create. Notice that after calling this + * constructor the file isn't opened yet, so writing packets will fail. For opening the file call open() + * @param[in] fileName The full path of the file + * @param[in] linkLayerType The link layer type all packet in this file will be based on. The default is Ethernet + */ + PcapFileWriterDevice(const std::string& fileName, LinkLayerType linkLayerType = LINKTYPE_ETHERNET); + + /** + * A destructor for this class + */ + ~PcapFileWriterDevice() {} + + /** + * Write a RawPacket to the file. Before using this method please verify the file is opened using open(). This method won't change the + * written packet + * @param[in] packet A reference for an existing RawPcket to write to the file + * @return True if a packet was written successfully. False will be returned if the file isn't opened + * or if the packet link layer type is different than the one defined for the file + * (in all cases, an error will be printed to log) + */ + bool writePacket(RawPacket const& packet); + + /** + * Write multiple RawPacket to the file. Before using this method please verify the file is opened using open(). This method won't change + * the written packets or the RawPacketVector instance + * @param[in] packets A reference for an existing RawPcketVector, all of its packets will be written to the file + * @return True if all packets were written successfully to the file. False will be returned if the file isn't opened (also, an error + * log will be printed) or if at least one of the packets wasn't written successfully to the file + */ + bool writePackets(const RawPacketVector& packets); + + //override methods + + /** + * Open the file in a write mode. If file doesn't exist, it will be created. If it does exist it will be + * overwritten, meaning all its current content will be deleted + * @return True if file was opened/created successfully or if file is already opened. False if opening the file failed for some reason + * (an error will be printed to log) + */ + virtual bool open(); + + /** + * Same as open(), but enables to open the file in append mode in which packets will be appended to the file + * instead of overwrite its current content. In append mode file must exist, otherwise opening will fail + * @param[in] appendMode A boolean indicating whether to open the file in append mode or not. If set to false + * this method will act exactly like open(). If set to true, file will be opened in append mode + * @return True of managed to open the file successfully. In case appendMode is set to true, false will be returned + * if file wasn't found or couldn't be read, if file type is not pcap, or if link type specified in c'tor is + * different from current file link type. In case appendMode is set to false, please refer to open() for return + * values + */ + bool open(bool appendMode); + + /** + * Flush and close the pacp file + */ + virtual void close(); + + /** + * Flush packets to disk. + */ + void flush(); + + /** + * Get statistics of packets written so far. + * @param[out] stats The stats struct where stats are returned + */ + virtual void getStatistics(PcapStats& stats) const; + }; + + + /** + * @class PcapNgFileWriterDevice + * A class for opening a pcap-ng file for writing or creating a new pcap-ng file and write packets to it. This class adds + * unique capabilities such as writing metadata attributes into the file header, adding comments per packet and opening + * the file in append mode where packets are added to a file instead of overriding it. This capabilities are part of the + * pcap-ng standard but aren't supported in most tools and libraries + */ + class PcapNgFileWriterDevice : public IFileWriterDevice + { + private: + void* m_LightPcapNg; + int m_CompressionLevel; + BpfFilterWrapper m_BpfWrapper; + + // private copy c'tor + PcapNgFileWriterDevice(const PcapFileWriterDevice& other); + PcapNgFileWriterDevice& operator=(const PcapNgFileWriterDevice& other); + + public: + + /** + * A constructor for this class that gets the pcap-ng full path file name to open for writing or create. Notice that after calling this + * constructor the file isn't opened yet, so writing packets will fail. For opening the file call open() + * @param[in] fileName The full path of the file + * @param[in] compressionLevel The compression level to use when writing the file, use 0 to disable compression or 10 for max compression. Default is 0 + */ + PcapNgFileWriterDevice(const std::string& fileName, int compressionLevel = 0); + + /** + * A destructor for this class + */ + virtual ~PcapNgFileWriterDevice() { close(); } + + /** + * Open the file in a write mode. If file doesn't exist, it will be created. If it does exist it will be + * overwritten, meaning all its current content will be deleted. As opposed to open(), this method also allows writing several + * metadata attributes that will be stored in the header of the file + * @param[in] os A string describing the operating system that was used to capture the packets. If this string is empty or null it + * will be ignored + * @param[in] hardware A string describing the hardware that was used to capture the packets. If this string is empty or null it + * will be ignored + * @param[in] captureApp A string describing the application that was used to capture the packets. If this string is empty or null it + * will be ignored + * @param[in] fileComment A string containing a user-defined comment that will be part of the metadata of the file. + * If this string is empty or null it will be ignored + * @return True if file was opened/created successfully or if file is already opened. False if opening the file failed for some reason + * (an error will be printed to log) + */ + bool open(const std::string& os, const std::string& hardware, const std::string& captureApp, const std::string& fileComment); + + /** + * The pcap-ng format allows adding a user-defined comment for each stored packet. This method writes a RawPacket to the file and + * adds a comment to it. Before using this method please verify the file is opened using open(). This method won't change the + * written packet or the input comment + * @param[in] packet A reference for an existing RawPcket to write to the file + * @param[in] comment The comment to be written for the packet. If this string is empty or null it will be ignored + * @return True if a packet was written successfully. False will be returned if the file isn't opened (an error will be printed to log) + */ + bool writePacket(RawPacket const& packet, const std::string& comment); + + //overridden methods + + /** + * Write a RawPacket to the file. Before using this method please verify the file is opened using open(). This method won't change the + * written packet + * @param[in] packet A reference for an existing RawPcket to write to the file + * @return True if a packet was written successfully. False will be returned if the file isn't opened (an error will be printed to log) + */ + bool writePacket(RawPacket const& packet); + + /** + * Write multiple RawPacket to the file. Before using this method please verify the file is opened using open(). This method won't change + * the written packets or the RawPacketVector instance + * @param[in] packets A reference for an existing RawPcketVector, all of its packets will be written to the file + * @return True if all packets were written successfully to the file. False will be returned if the file isn't opened (also, an error + * log will be printed) or if at least one of the packets wasn't written successfully to the file + */ + bool writePackets(const RawPacketVector& packets); + + /** + * Open the file in a write mode. If file doesn't exist, it will be created. If it does exist it will be + * overwritten, meaning all its current content will be deleted + * @return True if file was opened/created successfully or if file is already opened. False if opening the file failed for some reason + * (an error will be printed to log) + */ + bool open(); + + /** + * Same as open(), but enables to open the file in append mode in which packets will be appended to the file + * instead of overwrite its current content. In append mode file must exist, otherwise opening will fail + * @param[in] appendMode A boolean indicating whether to open the file in append mode or not. If set to false + * this method will act exactly like open(). If set to true, file will be opened in append mode + * @return True of managed to open the file successfully. In case appendMode is set to true, false will be returned + * if file wasn't found or couldn't be read, if file type is not pcap-ng. In case appendMode is set to false, please refer to open() + * for return values + */ + bool open(bool appendMode); + + /** + * Flush packets to the pcap-ng file + */ + void flush(); + + /** + * Flush and close the pcap-ng file + */ + void close(); + + /** + * Get statistics of packets written so far. + * @param[out] stats The stats struct where stats are returned + */ + void getStatistics(PcapStats& stats) const; + + /** + * Set a filter for PcapNG writer device. Only packets that match the filter will be persisted + * @param[in] filterAsString The filter to be set in Berkeley Packet Filter (BPF) syntax (http://biot.com/capstats/bpf.html) + * @return True if filter set successfully, false otherwise + */ + bool setFilter(std::string filterAsString); + + }; + +}// namespace pcpp + +#endif diff --git a/Pcap++/header/PcapFilter.h b/Pcap++/header/PcapFilter.h index 5a384bd707..60495452cd 100644 --- a/Pcap++/header/PcapFilter.h +++ b/Pcap++/header/PcapFilter.h @@ -1,803 +1,803 @@ -#ifndef PCAPP_FILTER -#define PCAPP_FILTER - -#include -#include -#include -#include "ProtocolType.h" -#include -#include "ArpLayer.h" -#include "RawPacket.h" - -//Forward Declaration - used in GeneralFilter -struct bpf_program; - -/** - * @file - * Most packet capture engines contain packet filtering capabilities. In order to set the filters there should be a known syntax user can use. - * The most popular syntax is Berkeley Packet Filter (BPF) - see more in here: http://en.wikipedia.org/wiki/Berkeley_Packet_Filter. - * Detailed explanation of the syntax can be found here: http://www.tcpdump.org/manpages/pcap-filter.7.html.
- * The problem with BPF is that, for my opinion, the syntax is too complicated and too poorly documented. In addition the BPF filter compilers - * may output syntax errors that are hard to understand. My experience with BPF was not good, so I decided to make the filters mechanism more - * structured, easier to understand and less error-prone by creating classes that represent filters. Each possible filter phrase is represented - * by a class. The filter, at the end, is that class.
- * For example: the filter "src host 1.1.1.1" will be represented by IPFilter instance; "dst port 80" will be represented by PortFilter, and - * so on.
- * So what about complex filters that involve "and", "or"? There are also 2 classes: AndFilter and OrFilter that can store more filters (in a - * composite idea) and connect them by "and" or "or". For example: "src host 1.1.1.1 and dst port 80" will be represented by an AndFilter that - * h olds IPFilter and PortFilter inside it - */ - -/** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - //Forward Declaration - used in GeneralFilter - class RawPacket; - - /** - * An enum that contains direction (source or destination) - */ - typedef enum - { - /** Source */ - SRC, - /** Destination */ - DST, - /** Source or destination */ - SRC_OR_DST - } Direction; - - - /** - * Supported operators enum - */ - typedef enum - { - /** Equals */ - EQUALS, - /** Not equals */ - NOT_EQUALS, - /** Greater than */ - GREATER_THAN, - /** Greater or equal */ - GREATER_OR_EQUAL, - /** Less than */ - LESS_THAN, - /** Less or equal */ - LESS_OR_EQUAL - } FilterOperator; - - /** - * @class BpfFilterWrapper - * 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; - bpf_program* m_Program; - - void freeProgram(); - - public: - - /** - * A c'tor for this class - */ - BpfFilterWrapper(); - - /** - * A d'tor for this class. Makes sure to clear the bpf_program object if was previously set. - */ - ~BpfFilterWrapper(); - - /** - * 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 - */ - 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". - * If the link type of the raw packet is different than the one set in setFilter(), the filter will be - * re-compiled and stored in the object. - * @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); - - /** - * 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 - * and stored in the object. - * @param[in] packetData A byte stream containing the packet data - * @param[in] packetDataLength The length in [bytes] of the byte stream - * @param[in] packetTimestamp The packet timestamp - * @param[in] linkType The packet link type - * @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); - }; - - /** - * @class GeneralFilter - * The base class for all filter classes. This class is virtual and abstract, hence cannot be instantiated.
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class GeneralFilter - { - protected: - BpfFilterWrapper m_BpfWrapper; - - public: - /** - * 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 - * @return No return value - */ - virtual void parseToString(std::string& result) = 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() {} - - /** - * Virtual destructor, frees the bpf program - */ - virtual ~GeneralFilter() {} - }; - - /** - * @class BPFStringFilter - * This class can be loaded with a BPF filter string and then can be used to verify the string is valid.
- */ - class BPFStringFilter : public GeneralFilter - { - private: - const std::string m_FilterStr; - - public: - explicit BPFStringFilter(const std::string& filterStr) : m_FilterStr(filterStr) {} - - virtual ~BPFStringFilter() {} - - /** - * 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 - * @return No return value - */ - virtual void parseToString(std::string& result); - - /** - * Verify the filter is valid - * @return True if the filter is valid or false otherwise - */ - bool verifyFilter(); - }; - - - /** - * @class IFilterWithDirection - * An abstract class that is the base class for all filters which contain a direction (source or destination). This class cannot be instantiated
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class IFilterWithDirection : public GeneralFilter - { - private: - Direction m_Dir; - protected: - void parseDirection(std::string& directionAsString); - Direction getDir() const { return m_Dir; } - explicit IFilterWithDirection(Direction dir) { m_Dir = dir; } - public: - /** - * Set the direction for the filter (source or destination) - * @param[in] dir The direction - */ - void setDirection(Direction dir) { m_Dir = dir; } - }; - - - /** - * @class IFilterWithOperator - * An abstract class that is the base class for all filters which contain an operator (e.g X equals Y; A is greater than B; Z1 not equals Z2, etc.). - * This class cannot be instantiated
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class IFilterWithOperator : public GeneralFilter - { - private: - FilterOperator m_Operator; - protected: - std::string parseOperator(); - FilterOperator getOperator() const { return m_Operator; } - explicit IFilterWithOperator(FilterOperator op) { m_Operator = op; } - public: - /** - * Set the operator for the filter - * @param[in] op The operator to set - */ - void setOperator(FilterOperator op) { m_Operator = op; } - }; - - - - /** - * @class IPFilter - * A class for representing IPv4 address filter, equivalent to "net src x.x.x.x" or "net dst x.x.x.x"
- * For deeper understanding of the filter concept please refer to PcapFilter.h - * @todo Add IPv6 filtering support - */ - class IPFilter : public IFilterWithDirection - { - private: - std::string m_Address; - std::string m_IPv4Mask; - int m_Len; - void convertToIPAddressWithMask(std::string& ipAddrmodified, std::string& mask) const; - void convertToIPAddressWithLen(std::string& ipAddrmodified) const; - public: - /** - * The basic constructor that creates the filter from an IPv4 address and direction (source or destination) - * @param[in] ipAddress The IPv4 address to build the filter with. If this address is not a valid IPv4 address an error will be - * written to log and parsing this filter will fail - * @param[in] dir The address direction to filter (source or destination) - */ - IPFilter(const std::string& ipAddress, Direction dir) : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(""), m_Len(0) {} - - /** - * A constructor that enable to filter only part of the address by using a mask (aka subnet). For example: "filter only IP addresses that matches - * the subnet 10.0.0.x" - * @param[in] ipAddress The IPv4 address to use. Only the part of the address that is not masked will be matched. For example: if the address - * is "1.2.3.4" and the mask is "255.255.255.0" than the part of the address that will be matched is "1.2.3.X". If this address is not a - * valid IPv4 address an error will be written to log and parsing this filter will fail - * @param[in] dir The address direction to filter (source or destination) - * @param[in] ipv4Mask The mask to use. Mask should also be in a valid IPv4 format (i.e x.x.x.x), otherwise parsing this filter will fail - */ - IPFilter(const std::string& ipAddress, Direction dir, const std::string& ipv4Mask) : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(ipv4Mask), m_Len(0) {} - - /** - * A constructor that enables to filter by a subnet. For example: "filter only IP addresses that matches the subnet 10.0.0.3/24" which means - * the part of the address that will be matched is "10.0.0.X" - * @param[in] ipAddress The IPv4 address to use. Only the part of the address that is not masked will be matched. For example: if the address - * is "1.2.3.4" and the subnet is "/24" than the part of the address that will be matched is "1.2.3.X". If this address is not a - * valid IPv4 address an error will be written to log and parsing this filter will fail - * @param[in] dir The address direction to filter (source or destination) - * @param[in] len The subnet to use (e.g "/24") - */ - IPFilter(const std::string& ipAddress, Direction dir, int len) : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(""), m_Len(len) {} - - void parseToString(std::string& result); - - /** - * Set the IPv4 address - * @param[in] ipAddress The IPv4 address to build the filter with. If this address is not a valid IPv4 address an error will be - * written to log and parsing this filter will fail - */ - void setAddr(const std::string& ipAddress) { m_Address = ipAddress; } - - /** - * Set the IPv4 mask - * @param[in] ipv4Mask The mask to use. Mask should also be in a valid IPv4 format (i.e x.x.x.x), otherwise parsing this filter will fail - */ - void setMask(const std::string& ipv4Mask) { m_IPv4Mask = ipv4Mask; m_Len = 0; } - - /** - * Set the subnet - * @param[in] len The subnet to use (e.g "/24") - */ - void setLen(int len) { m_IPv4Mask = ""; m_Len = len; } - }; - - - - /** - * @class IPv4IDFilter - * A class for filtering IPv4 traffic by IP ID field of the IPv4 protocol, For example: - * "filter only IPv4 traffic which IP ID is greater than 1234"
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class IPv4IDFilter : public IFilterWithOperator - { - private: - uint16_t m_IpID; - public: - /** - * A constructor that gets the IP ID to filter and the operator and creates the filter out of them - * @param[in] ipID The IP ID to filter - * @param[in] op The operator to use (e.g "equal", "greater than", etc.) - */ - IPv4IDFilter(uint16_t ipID, FilterOperator op) : IFilterWithOperator(op), m_IpID(ipID) {} - - void parseToString(std::string& result); - - /** - * Set the IP ID to filter - * @param[in] ipID The IP ID to filter - */ - void setIpID(uint16_t ipID) { m_IpID = ipID; } - }; - - - - /** - * @class IPv4TotalLengthFilter - * A class for filtering IPv4 traffic by "total length" field of the IPv4 protocol, For example: - * "filter only IPv4 traffic which "total length" value is less than 60B"
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class IPv4TotalLengthFilter : public IFilterWithOperator - { - private: - uint16_t m_TotalLength; - public: - /** - * A constructor that gets the total length to filter and the operator and creates the filter out of them - * @param[in] totalLength The total length value to filter - * @param[in] op The operator to use (e.g "equal", "greater than", etc.) - */ - IPv4TotalLengthFilter(uint16_t totalLength, FilterOperator op) : IFilterWithOperator(op), m_TotalLength(totalLength) {} - - void parseToString(std::string& result); - - /** - * Set the total length value - * @param[in] totalLength The total length value to filter - */ - void setTotalLength(uint16_t totalLength) { m_TotalLength = totalLength; } - }; - - - - /** - * @class PortFilter - * A class for filtering TCP or UDP traffic by port, for example: "dst port 80" or "src port 12345"
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class PortFilter : public IFilterWithDirection - { - private: - std::string m_Port; - void portToString(uint16_t portAsInt); - public: - /** - * A constructor that gets the port and the direction and creates the filter - * @param[in] port The port to create the filter with - * @param[in] dir The port direction to filter (source or destination) - */ - PortFilter(uint16_t port, Direction dir); - - void parseToString(std::string& result); - - /** - * Set the port - * @param[in] port The port to create the filter with - */ - void setPort(uint16_t port) { portToString(port); } - }; - - - - /** - * @class PortRangeFilter - * A class for filtering TCP or UDP port ranges, meaning match only packets which port is within this range, for example: "src portrange 1000-2000" - * will match only TCP or UDP traffic which source port is in the range of 1000 - 2000
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class PortRangeFilter : public IFilterWithDirection - { - private: - uint16_t m_FromPort; - uint16_t m_ToPort; - public: - /** - * A constructor that gets the port range the the direction and creates the filter with them - * @param[in] fromPort The lower end of the port range - * @param[in] toPort The higher end of the port range - * @param[in] dir The port range direction to filter (source or destination) - */ - PortRangeFilter(uint16_t fromPort, uint16_t toPort, Direction dir) : IFilterWithDirection(dir), m_FromPort(fromPort), m_ToPort(toPort) {} - - void parseToString(std::string& result); - - /** - * 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; } - - /** - * Set the higher end of the port range - * @param[in] toPort The higher end of the port range - */ - void setToPort(uint16_t toPort) { m_ToPort = toPort; } - }; - - - - /** - * @class MacAddressFilter - * A class for filtering Ethernet traffic by MAC addresses, for example: "ether src 12:34:56:78:90:12" or "ether dst "10:29:38:47:56:10:29"
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class MacAddressFilter : public IFilterWithDirection - { - private: - MacAddress m_MacAddress; - public: - /** - * A constructor that gets the MAC address and the direction and creates the filter with them - * @param[in] address The MAC address to use for filtering - * @param[in] dir The MAC address direction to filter (source or destination) - */ - MacAddressFilter(MacAddress address, Direction dir) : IFilterWithDirection(dir), m_MacAddress(address) {} - - void parseToString(std::string& result); - - /** - * Set the MAC address - * @param[in] address The MAC address to use for filtering - */ - void setMacAddress(MacAddress address) { m_MacAddress = address; } - }; - - - - /** - * @class EtherTypeFilter - * A class for filtering by EtherType field of the Ethernet protocol. This enables to filter packets from certain protocols, such as ARP, IPv4, - * IPv6, VLAN tags, etc.
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class EtherTypeFilter : public GeneralFilter - { - private: - uint16_t m_EtherType; - public: - /** - * A constructor that gets the EtherType and creates the filter with it - * @param[in] etherType The EtherType value to create the filter with - */ - explicit EtherTypeFilter(uint16_t etherType) : m_EtherType(etherType) {} - - void parseToString(std::string& result); - - /** - * Set the EtherType value - * @param[in] etherType The EtherType value to create the filter with - */ - void setEtherType(uint16_t etherType) { m_EtherType = etherType; } - }; - - - - /** - * @class AndFilter - * A class for connecting several filters into one filter with logical "and" between them. For example: if the 2 filters are: "IPv4 address = - * x.x.x.x" + "TCP port dst = 80", then the new filter will be: "IPv4 address = x.x.x.x _AND_ TCP port dst = 80"
- * This class follows the composite design pattern
- * For deeper understanding of the filter concept please refer to PcapFilter.h - * @todo add some methods: "addFilter", "removeFilter", "clearAllFilter" - */ - class AndFilter : public GeneralFilter - { - private: - std::vector m_FilterList; - public: - - /** - * An empty constructor for this class. Use addFilter() to add filters to the and condition - */ - AndFilter() {} - - /** - * A constructor that gets a list of pointers to filters and creates one filter from all filters with logical "and" between them - * @param[in] filters The list of pointers to filters - */ - explicit AndFilter(std::vector& filters); - - /** - * Add filter to the and condition - * @param[in] filter The filter to add - */ - void addFilter(GeneralFilter* filter) { m_FilterList.push_back(filter); } - - /** - * Remove the current filters and set new ones - * @param[in] filters The new filters to set. The previous ones will be removed - */ - void setFilters(std::vector& filters); - - void parseToString(std::string& result); - }; - - - - /** - * @class OrFilter - * A class for connecting several filters into one filter with logical "or" between them. For example: if the 2 filters are: "IPv4 address = - * x.x.x.x" + "TCP port dst = 80", then the new filter will be: "IPv4 address = x.x.x.x _OR_ TCP port dst = 80"
- * This class follows the composite design pattern
- * For deeper understanding of the filter concept please refer to PcapFilter.h - * @todo add some methods: "addFilter", "removeFilter", "clearAllFilter" - */ - class OrFilter : public GeneralFilter - { - private: - std::vector m_FilterList; - public: - - /** - * An empty constructor for this class. Use addFilter() to add filters to the or condition - */ - OrFilter() {} - - /** - * A constructor that gets a list of pointers to filters and creates one filter from all filters with logical "or" between them - * @param[in] filters The list of pointers to filters - */ - explicit OrFilter(std::vector& filters); - - /** - * Add filter to the or condition - * @param[in] filter The filter to add - */ - void addFilter(GeneralFilter* filter) { m_FilterList.push_back(filter); } - - void parseToString(std::string& result); - }; - - - - /** - * @class NotFilter - * A class for creating a filter which is inverse to another filter
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class NotFilter : public GeneralFilter - { - private: - GeneralFilter* m_FilterToInverse; - public: - /** - * A constructor that gets a pointer to a filter and create the inverse version of it - * @param[in] filterToInverse A pointer to filter which the created filter be the inverse of - */ - explicit NotFilter(GeneralFilter* filterToInverse) { m_FilterToInverse = filterToInverse; } - - void parseToString(std::string& result); - - /** - * 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; } - }; - - - - /** - * @class ProtoFilter - * A class for filtering traffic by protocol. Notice not all protocols are supported, only the following are supported: - * ::TCP, ::UDP, ::ICMP, ::VLAN, ::IPv4, ::IPv6, ::ARP, ::Ethernet, ::GRE (distinguish between ::GREv0 and ::GREv1 is not supported), - * ::IGMP (distinguish between ::IGMPv1, ::IGMPv2 and ::IGMPv3 is not supported).
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class ProtoFilter : public GeneralFilter - { - private: - ProtocolType m_Proto; - public: - /** - * A constructor that gets the protocol and creates the filter - * @param[in] proto The protocol to filter, only packets matching this protocol will be received. Please note not all protocols are - * supported. List of supported protocols is found in the class description - */ - explicit ProtoFilter(ProtocolType proto) : m_Proto(proto) {} - - void parseToString(std::string& result); - - /** - * Set the protocol to filter with - * @param[in] proto The protocol to filter, only packets matching this protocol will be received. Please note not all protocols are - * supported. List of supported protocols is found in the class description - */ - void setProto(ProtocolType proto) { m_Proto = proto; } - }; - - - - /** - * @class ArpFilter - * A class for filtering ARP packets according the ARP opcode. When using this filter only ARP packets with the relevant opcode will be - * received
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class ArpFilter : public GeneralFilter - { - private: - ArpOpcode m_OpCode; - public: - /** - * A constructor that get the ARP opcode and creates the filter - * @param[in] opCode The ARP opcode: ::ARP_REQUEST or ::ARP_REPLY - */ - explicit ArpFilter(ArpOpcode opCode) : m_OpCode(opCode) {} - - void parseToString(std::string& result); - - /** - * Set the ARP opcode - * @param[in] opCode The ARP opcode: ::ARP_REQUEST or ::ARP_REPLY - */ - void setOpCode(ArpOpcode opCode) { m_OpCode = opCode; } - }; - - - - /** - * @class VlanFilter - * A class for filtering VLAN tagged packets by VLAN ID. When using this filter only packets tagged with VLAN which has the specific VLAN ID - * will be received
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class VlanFilter : public GeneralFilter - { - private: - uint16_t m_VlanID; - public: - /** - * A constructor the gets the VLAN ID and creates the filter - * @param[in] vlanId The VLAN ID to use for the filter - */ - explicit VlanFilter(uint16_t vlanId) : m_VlanID(vlanId) {} - - void parseToString(std::string& result); - - /** - * 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; } - }; - - - - /** - * @class TcpFlagsFilter - * A class for filtering only TCP packets which certain TCP flags are set in them
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class TcpFlagsFilter : public GeneralFilter - { - public: - /** - * An enum of all TCP flags that can be use in the filter - */ - enum TcpFlags - { - /** TCP FIN flag */ - tcpFin = 1, - /** TCP SYN flag */ - tcpSyn = 2, - /** TCP RST flag */ - tcpRst = 4, - /** TCP PSH flag */ - tcpPush = 8, - /** TCP ACK flag */ - tcpAck = 16, - /** TCP URG flag */ - tcpUrg = 32 - }; - - /** - * An enum for representing 2 type of matches: match only packets that contain all flags defined in the filter or match packets that - * contain at least one of the flags defined in the filter - */ - enum MatchOptions - { - /** Match only packets that contain all flags defined in the filter */ - MatchAll, - /** Match packets that contain at least one of the flags defined in the filter */ - MatchOneAtLeast - }; - private: - uint8_t m_TcpFlagsBitMask; - MatchOptions m_MatchOption; - public: - /** - * A constructor that gets a 1-byte bitmask containing all TCP flags participating in the filter and the match option, and - * creates the filter - * @param[in] tcpFlagBitMask A 1-byte bitmask containing all TCP flags participating in the filter. This parameter can contain the - * following value for example: TcpFlagsFilter::tcpSyn | TcpFlagsFilter::tcpAck | TcpFlagsFilter::tcpUrg - * @param[in] matchOption The match option: TcpFlagsFilter::MatchAll or TcpFlagsFilter::MatchOneAtLeast - */ - TcpFlagsFilter(uint8_t tcpFlagBitMask, MatchOptions matchOption) : m_TcpFlagsBitMask(tcpFlagBitMask), m_MatchOption(matchOption) {} - - /** - * Set the TCP flags and the match option - * @param[in] tcpFlagBitMask A 1-byte bitmask containing all TCP flags participating in the filter. This parameter can contain the - * following value for example: TcpFlagsFilter::tcpSyn | TcpFlagsFilter::tcpAck | TcpFlagsFilter::tcpUrg - * @param[in] matchOption The match option: TcpFlagsFilter::MatchAll or TcpFlagsFilter::MatchOneAtLeast - */ - void setTcpFlagsBitMask(uint8_t tcpFlagBitMask, MatchOptions matchOption) { m_TcpFlagsBitMask = tcpFlagBitMask; m_MatchOption = matchOption; } - - void parseToString(std::string& result); - }; - - - - /** - * @class TcpWindowSizeFilter - * A class for filtering TCP packets that matches TCP window-size criteria
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class TcpWindowSizeFilter : public IFilterWithOperator - { - private: - uint16_t m_WindowSize; - public: - /** - * A constructor that get the window-size and operator and creates the filter. For example: "filter all TCP packets with window-size - * less than 1000" - * @param[in] windowSize The window-size value that will be used in the filter - * @param[in] op The operator to use (e.g "equal", "greater than", etc.) - */ - TcpWindowSizeFilter(uint16_t windowSize, FilterOperator op) : IFilterWithOperator(op), m_WindowSize(windowSize) {} - - void parseToString(std::string& result); - - /** - * 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; } - }; - - - - /** - * @class UdpLengthFilter - * A class for filtering UDP packets that matches UDP length criteria
- * For deeper understanding of the filter concept please refer to PcapFilter.h - */ - class UdpLengthFilter : public IFilterWithOperator - { - private: - uint16_t m_Length; - public: - /** - * A constructor that get the UDP length and operator and creates the filter. For example: "filter all UDP packets with length - * greater or equal to 500" - * @param[in] length The length value that will be used in the filter - * @param[in] op The operator to use (e.g "equal", "greater than", etc.) - */ - UdpLengthFilter(uint16_t length, FilterOperator op) : IFilterWithOperator(op), m_Length(length) {} - - void parseToString(std::string& result); - - /** - * Set length value - * @param[in] length The length value that will be used in the filter - */ - void setLength(uint16_t length) { m_Length = length; } - }; - -} // namespace pcpp - -#endif +#ifndef PCAPP_FILTER +#define PCAPP_FILTER + +#include +#include +#include +#include "ProtocolType.h" +#include +#include "ArpLayer.h" +#include "RawPacket.h" + +//Forward Declaration - used in GeneralFilter +struct bpf_program; + +/** + * @file + * Most packet capture engines contain packet filtering capabilities. In order to set the filters there should be a known syntax user can use. + * The most popular syntax is Berkeley Packet Filter (BPF) - see more in here: http://en.wikipedia.org/wiki/Berkeley_Packet_Filter. + * Detailed explanation of the syntax can be found here: http://www.tcpdump.org/manpages/pcap-filter.7.html.
+ * The problem with BPF is that, for my opinion, the syntax is too complicated and too poorly documented. In addition the BPF filter compilers + * may output syntax errors that are hard to understand. My experience with BPF was not good, so I decided to make the filters mechanism more + * structured, easier to understand and less error-prone by creating classes that represent filters. Each possible filter phrase is represented + * by a class. The filter, at the end, is that class.
+ * For example: the filter "src host 1.1.1.1" will be represented by IPFilter instance; "dst port 80" will be represented by PortFilter, and + * so on.
+ * So what about complex filters that involve "and", "or"? There are also 2 classes: AndFilter and OrFilter that can store more filters (in a + * composite idea) and connect them by "and" or "or". For example: "src host 1.1.1.1 and dst port 80" will be represented by an AndFilter that + * h olds IPFilter and PortFilter inside it + */ + +/** +* \namespace pcpp +* \brief The main namespace for the PcapPlusPlus lib +*/ +namespace pcpp +{ + //Forward Declaration - used in GeneralFilter + class RawPacket; + + /** + * An enum that contains direction (source or destination) + */ + typedef enum + { + /** Source */ + SRC, + /** Destination */ + DST, + /** Source or destination */ + SRC_OR_DST + } Direction; + + + /** + * Supported operators enum + */ + typedef enum + { + /** Equals */ + EQUALS, + /** Not equals */ + NOT_EQUALS, + /** Greater than */ + GREATER_THAN, + /** Greater or equal */ + GREATER_OR_EQUAL, + /** Less than */ + LESS_THAN, + /** Less or equal */ + LESS_OR_EQUAL + } FilterOperator; + + /** + * @class BpfFilterWrapper + * 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; + bpf_program* m_Program; + + void freeProgram(); + + public: + + /** + * A c'tor for this class + */ + BpfFilterWrapper(); + + /** + * A d'tor for this class. Makes sure to clear the bpf_program object if was previously set. + */ + ~BpfFilterWrapper(); + + /** + * 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 + */ + 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". + * If the link type of the raw packet is different than the one set in setFilter(), the filter will be + * re-compiled and stored in the object. + * @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); + + /** + * 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 + * and stored in the object. + * @param[in] packetData A byte stream containing the packet data + * @param[in] packetDataLength The length in [bytes] of the byte stream + * @param[in] packetTimestamp The packet timestamp + * @param[in] linkType The packet link type + * @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); + }; + + /** + * @class GeneralFilter + * The base class for all filter classes. This class is virtual and abstract, hence cannot be instantiated.
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class GeneralFilter + { + protected: + BpfFilterWrapper m_BpfWrapper; + + public: + /** + * 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 + * @return No return value + */ + virtual void parseToString(std::string& result) = 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() {} + + /** + * Virtual destructor, frees the bpf program + */ + virtual ~GeneralFilter() {} + }; + + /** + * @class BPFStringFilter + * This class can be loaded with a BPF filter string and then can be used to verify the string is valid.
+ */ + class BPFStringFilter : public GeneralFilter + { + private: + const std::string m_FilterStr; + + public: + explicit BPFStringFilter(const std::string& filterStr) : m_FilterStr(filterStr) {} + + virtual ~BPFStringFilter() {} + + /** + * 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 + * @return No return value + */ + virtual void parseToString(std::string& result); + + /** + * Verify the filter is valid + * @return True if the filter is valid or false otherwise + */ + bool verifyFilter(); + }; + + + /** + * @class IFilterWithDirection + * An abstract class that is the base class for all filters which contain a direction (source or destination). This class cannot be instantiated
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class IFilterWithDirection : public GeneralFilter + { + private: + Direction m_Dir; + protected: + void parseDirection(std::string& directionAsString); + Direction getDir() const { return m_Dir; } + explicit IFilterWithDirection(Direction dir) { m_Dir = dir; } + public: + /** + * Set the direction for the filter (source or destination) + * @param[in] dir The direction + */ + void setDirection(Direction dir) { m_Dir = dir; } + }; + + + /** + * @class IFilterWithOperator + * An abstract class that is the base class for all filters which contain an operator (e.g X equals Y; A is greater than B; Z1 not equals Z2, etc.). + * This class cannot be instantiated
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class IFilterWithOperator : public GeneralFilter + { + private: + FilterOperator m_Operator; + protected: + std::string parseOperator(); + FilterOperator getOperator() const { return m_Operator; } + explicit IFilterWithOperator(FilterOperator op) { m_Operator = op; } + public: + /** + * Set the operator for the filter + * @param[in] op The operator to set + */ + void setOperator(FilterOperator op) { m_Operator = op; } + }; + + + + /** + * @class IPFilter + * A class for representing IPv4 address filter, equivalent to "net src x.x.x.x" or "net dst x.x.x.x"
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + * @todo Add IPv6 filtering support + */ + class IPFilter : public IFilterWithDirection + { + private: + std::string m_Address; + std::string m_IPv4Mask; + int m_Len; + void convertToIPAddressWithMask(std::string& ipAddrmodified, std::string& mask) const; + void convertToIPAddressWithLen(std::string& ipAddrmodified) const; + public: + /** + * The basic constructor that creates the filter from an IPv4 address and direction (source or destination) + * @param[in] ipAddress The IPv4 address to build the filter with. If this address is not a valid IPv4 address an error will be + * written to log and parsing this filter will fail + * @param[in] dir The address direction to filter (source or destination) + */ + IPFilter(const std::string& ipAddress, Direction dir) : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(""), m_Len(0) {} + + /** + * A constructor that enable to filter only part of the address by using a mask (aka subnet). For example: "filter only IP addresses that matches + * the subnet 10.0.0.x" + * @param[in] ipAddress The IPv4 address to use. Only the part of the address that is not masked will be matched. For example: if the address + * is "1.2.3.4" and the mask is "255.255.255.0" than the part of the address that will be matched is "1.2.3.X". If this address is not a + * valid IPv4 address an error will be written to log and parsing this filter will fail + * @param[in] dir The address direction to filter (source or destination) + * @param[in] ipv4Mask The mask to use. Mask should also be in a valid IPv4 format (i.e x.x.x.x), otherwise parsing this filter will fail + */ + IPFilter(const std::string& ipAddress, Direction dir, const std::string& ipv4Mask) : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(ipv4Mask), m_Len(0) {} + + /** + * A constructor that enables to filter by a subnet. For example: "filter only IP addresses that matches the subnet 10.0.0.3/24" which means + * the part of the address that will be matched is "10.0.0.X" + * @param[in] ipAddress The IPv4 address to use. Only the part of the address that is not masked will be matched. For example: if the address + * is "1.2.3.4" and the subnet is "/24" than the part of the address that will be matched is "1.2.3.X". If this address is not a + * valid IPv4 address an error will be written to log and parsing this filter will fail + * @param[in] dir The address direction to filter (source or destination) + * @param[in] len The subnet to use (e.g "/24") + */ + IPFilter(const std::string& ipAddress, Direction dir, int len) : IFilterWithDirection(dir), m_Address(ipAddress), m_IPv4Mask(""), m_Len(len) {} + + void parseToString(std::string& result); + + /** + * Set the IPv4 address + * @param[in] ipAddress The IPv4 address to build the filter with. If this address is not a valid IPv4 address an error will be + * written to log and parsing this filter will fail + */ + void setAddr(const std::string& ipAddress) { m_Address = ipAddress; } + + /** + * Set the IPv4 mask + * @param[in] ipv4Mask The mask to use. Mask should also be in a valid IPv4 format (i.e x.x.x.x), otherwise parsing this filter will fail + */ + void setMask(const std::string& ipv4Mask) { m_IPv4Mask = ipv4Mask; m_Len = 0; } + + /** + * Set the subnet + * @param[in] len The subnet to use (e.g "/24") + */ + void setLen(int len) { m_IPv4Mask = ""; m_Len = len; } + }; + + + + /** + * @class IPv4IDFilter + * A class for filtering IPv4 traffic by IP ID field of the IPv4 protocol, For example: + * "filter only IPv4 traffic which IP ID is greater than 1234"
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class IPv4IDFilter : public IFilterWithOperator + { + private: + uint16_t m_IpID; + public: + /** + * A constructor that gets the IP ID to filter and the operator and creates the filter out of them + * @param[in] ipID The IP ID to filter + * @param[in] op The operator to use (e.g "equal", "greater than", etc.) + */ + IPv4IDFilter(uint16_t ipID, FilterOperator op) : IFilterWithOperator(op), m_IpID(ipID) {} + + void parseToString(std::string& result); + + /** + * Set the IP ID to filter + * @param[in] ipID The IP ID to filter + */ + void setIpID(uint16_t ipID) { m_IpID = ipID; } + }; + + + + /** + * @class IPv4TotalLengthFilter + * A class for filtering IPv4 traffic by "total length" field of the IPv4 protocol, For example: + * "filter only IPv4 traffic which "total length" value is less than 60B"
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class IPv4TotalLengthFilter : public IFilterWithOperator + { + private: + uint16_t m_TotalLength; + public: + /** + * A constructor that gets the total length to filter and the operator and creates the filter out of them + * @param[in] totalLength The total length value to filter + * @param[in] op The operator to use (e.g "equal", "greater than", etc.) + */ + IPv4TotalLengthFilter(uint16_t totalLength, FilterOperator op) : IFilterWithOperator(op), m_TotalLength(totalLength) {} + + void parseToString(std::string& result); + + /** + * Set the total length value + * @param[in] totalLength The total length value to filter + */ + void setTotalLength(uint16_t totalLength) { m_TotalLength = totalLength; } + }; + + + + /** + * @class PortFilter + * A class for filtering TCP or UDP traffic by port, for example: "dst port 80" or "src port 12345"
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class PortFilter : public IFilterWithDirection + { + private: + std::string m_Port; + void portToString(uint16_t portAsInt); + public: + /** + * A constructor that gets the port and the direction and creates the filter + * @param[in] port The port to create the filter with + * @param[in] dir The port direction to filter (source or destination) + */ + PortFilter(uint16_t port, Direction dir); + + void parseToString(std::string& result); + + /** + * Set the port + * @param[in] port The port to create the filter with + */ + void setPort(uint16_t port) { portToString(port); } + }; + + + + /** + * @class PortRangeFilter + * A class for filtering TCP or UDP port ranges, meaning match only packets which port is within this range, for example: "src portrange 1000-2000" + * will match only TCP or UDP traffic which source port is in the range of 1000 - 2000
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class PortRangeFilter : public IFilterWithDirection + { + private: + uint16_t m_FromPort; + uint16_t m_ToPort; + public: + /** + * A constructor that gets the port range the the direction and creates the filter with them + * @param[in] fromPort The lower end of the port range + * @param[in] toPort The higher end of the port range + * @param[in] dir The port range direction to filter (source or destination) + */ + PortRangeFilter(uint16_t fromPort, uint16_t toPort, Direction dir) : IFilterWithDirection(dir), m_FromPort(fromPort), m_ToPort(toPort) {} + + void parseToString(std::string& result); + + /** + * 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; } + + /** + * Set the higher end of the port range + * @param[in] toPort The higher end of the port range + */ + void setToPort(uint16_t toPort) { m_ToPort = toPort; } + }; + + + + /** + * @class MacAddressFilter + * A class for filtering Ethernet traffic by MAC addresses, for example: "ether src 12:34:56:78:90:12" or "ether dst "10:29:38:47:56:10:29"
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class MacAddressFilter : public IFilterWithDirection + { + private: + MacAddress m_MacAddress; + public: + /** + * A constructor that gets the MAC address and the direction and creates the filter with them + * @param[in] address The MAC address to use for filtering + * @param[in] dir The MAC address direction to filter (source or destination) + */ + MacAddressFilter(MacAddress address, Direction dir) : IFilterWithDirection(dir), m_MacAddress(address) {} + + void parseToString(std::string& result); + + /** + * Set the MAC address + * @param[in] address The MAC address to use for filtering + */ + void setMacAddress(MacAddress address) { m_MacAddress = address; } + }; + + + + /** + * @class EtherTypeFilter + * A class for filtering by EtherType field of the Ethernet protocol. This enables to filter packets from certain protocols, such as ARP, IPv4, + * IPv6, VLAN tags, etc.
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class EtherTypeFilter : public GeneralFilter + { + private: + uint16_t m_EtherType; + public: + /** + * A constructor that gets the EtherType and creates the filter with it + * @param[in] etherType The EtherType value to create the filter with + */ + explicit EtherTypeFilter(uint16_t etherType) : m_EtherType(etherType) {} + + void parseToString(std::string& result); + + /** + * Set the EtherType value + * @param[in] etherType The EtherType value to create the filter with + */ + void setEtherType(uint16_t etherType) { m_EtherType = etherType; } + }; + + + + /** + * @class AndFilter + * A class for connecting several filters into one filter with logical "and" between them. For example: if the 2 filters are: "IPv4 address = + * x.x.x.x" + "TCP port dst = 80", then the new filter will be: "IPv4 address = x.x.x.x _AND_ TCP port dst = 80"
+ * This class follows the composite design pattern
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + * @todo add some methods: "addFilter", "removeFilter", "clearAllFilter" + */ + class AndFilter : public GeneralFilter + { + private: + std::vector m_FilterList; + public: + + /** + * An empty constructor for this class. Use addFilter() to add filters to the and condition + */ + AndFilter() {} + + /** + * A constructor that gets a list of pointers to filters and creates one filter from all filters with logical "and" between them + * @param[in] filters The list of pointers to filters + */ + explicit AndFilter(std::vector& filters); + + /** + * Add filter to the and condition + * @param[in] filter The filter to add + */ + void addFilter(GeneralFilter* filter) { m_FilterList.push_back(filter); } + + /** + * Remove the current filters and set new ones + * @param[in] filters The new filters to set. The previous ones will be removed + */ + void setFilters(std::vector& filters); + + void parseToString(std::string& result); + }; + + + + /** + * @class OrFilter + * A class for connecting several filters into one filter with logical "or" between them. For example: if the 2 filters are: "IPv4 address = + * x.x.x.x" + "TCP port dst = 80", then the new filter will be: "IPv4 address = x.x.x.x _OR_ TCP port dst = 80"
+ * This class follows the composite design pattern
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + * @todo add some methods: "addFilter", "removeFilter", "clearAllFilter" + */ + class OrFilter : public GeneralFilter + { + private: + std::vector m_FilterList; + public: + + /** + * An empty constructor for this class. Use addFilter() to add filters to the or condition + */ + OrFilter() {} + + /** + * A constructor that gets a list of pointers to filters and creates one filter from all filters with logical "or" between them + * @param[in] filters The list of pointers to filters + */ + explicit OrFilter(std::vector& filters); + + /** + * Add filter to the or condition + * @param[in] filter The filter to add + */ + void addFilter(GeneralFilter* filter) { m_FilterList.push_back(filter); } + + void parseToString(std::string& result); + }; + + + + /** + * @class NotFilter + * A class for creating a filter which is inverse to another filter
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class NotFilter : public GeneralFilter + { + private: + GeneralFilter* m_FilterToInverse; + public: + /** + * A constructor that gets a pointer to a filter and create the inverse version of it + * @param[in] filterToInverse A pointer to filter which the created filter be the inverse of + */ + explicit NotFilter(GeneralFilter* filterToInverse) { m_FilterToInverse = filterToInverse; } + + void parseToString(std::string& result); + + /** + * 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; } + }; + + + + /** + * @class ProtoFilter + * A class for filtering traffic by protocol. Notice not all protocols are supported, only the following are supported: + * ::TCP, ::UDP, ::ICMP, ::VLAN, ::IPv4, ::IPv6, ::ARP, ::Ethernet, ::GRE (distinguish between ::GREv0 and ::GREv1 is not supported), + * ::IGMP (distinguish between ::IGMPv1, ::IGMPv2 and ::IGMPv3 is not supported).
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class ProtoFilter : public GeneralFilter + { + private: + ProtocolType m_Proto; + public: + /** + * A constructor that gets the protocol and creates the filter + * @param[in] proto The protocol to filter, only packets matching this protocol will be received. Please note not all protocols are + * supported. List of supported protocols is found in the class description + */ + explicit ProtoFilter(ProtocolType proto) : m_Proto(proto) {} + + void parseToString(std::string& result); + + /** + * Set the protocol to filter with + * @param[in] proto The protocol to filter, only packets matching this protocol will be received. Please note not all protocols are + * supported. List of supported protocols is found in the class description + */ + void setProto(ProtocolType proto) { m_Proto = proto; } + }; + + + + /** + * @class ArpFilter + * A class for filtering ARP packets according the ARP opcode. When using this filter only ARP packets with the relevant opcode will be + * received
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class ArpFilter : public GeneralFilter + { + private: + ArpOpcode m_OpCode; + public: + /** + * A constructor that get the ARP opcode and creates the filter + * @param[in] opCode The ARP opcode: ::ARP_REQUEST or ::ARP_REPLY + */ + explicit ArpFilter(ArpOpcode opCode) : m_OpCode(opCode) {} + + void parseToString(std::string& result); + + /** + * Set the ARP opcode + * @param[in] opCode The ARP opcode: ::ARP_REQUEST or ::ARP_REPLY + */ + void setOpCode(ArpOpcode opCode) { m_OpCode = opCode; } + }; + + + + /** + * @class VlanFilter + * A class for filtering VLAN tagged packets by VLAN ID. When using this filter only packets tagged with VLAN which has the specific VLAN ID + * will be received
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class VlanFilter : public GeneralFilter + { + private: + uint16_t m_VlanID; + public: + /** + * A constructor the gets the VLAN ID and creates the filter + * @param[in] vlanId The VLAN ID to use for the filter + */ + explicit VlanFilter(uint16_t vlanId) : m_VlanID(vlanId) {} + + void parseToString(std::string& result); + + /** + * 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; } + }; + + + + /** + * @class TcpFlagsFilter + * A class for filtering only TCP packets which certain TCP flags are set in them
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class TcpFlagsFilter : public GeneralFilter + { + public: + /** + * An enum of all TCP flags that can be use in the filter + */ + enum TcpFlags + { + /** TCP FIN flag */ + tcpFin = 1, + /** TCP SYN flag */ + tcpSyn = 2, + /** TCP RST flag */ + tcpRst = 4, + /** TCP PSH flag */ + tcpPush = 8, + /** TCP ACK flag */ + tcpAck = 16, + /** TCP URG flag */ + tcpUrg = 32 + }; + + /** + * An enum for representing 2 type of matches: match only packets that contain all flags defined in the filter or match packets that + * contain at least one of the flags defined in the filter + */ + enum MatchOptions + { + /** Match only packets that contain all flags defined in the filter */ + MatchAll, + /** Match packets that contain at least one of the flags defined in the filter */ + MatchOneAtLeast + }; + private: + uint8_t m_TcpFlagsBitMask; + MatchOptions m_MatchOption; + public: + /** + * A constructor that gets a 1-byte bitmask containing all TCP flags participating in the filter and the match option, and + * creates the filter + * @param[in] tcpFlagBitMask A 1-byte bitmask containing all TCP flags participating in the filter. This parameter can contain the + * following value for example: TcpFlagsFilter::tcpSyn | TcpFlagsFilter::tcpAck | TcpFlagsFilter::tcpUrg + * @param[in] matchOption The match option: TcpFlagsFilter::MatchAll or TcpFlagsFilter::MatchOneAtLeast + */ + TcpFlagsFilter(uint8_t tcpFlagBitMask, MatchOptions matchOption) : m_TcpFlagsBitMask(tcpFlagBitMask), m_MatchOption(matchOption) {} + + /** + * Set the TCP flags and the match option + * @param[in] tcpFlagBitMask A 1-byte bitmask containing all TCP flags participating in the filter. This parameter can contain the + * following value for example: TcpFlagsFilter::tcpSyn | TcpFlagsFilter::tcpAck | TcpFlagsFilter::tcpUrg + * @param[in] matchOption The match option: TcpFlagsFilter::MatchAll or TcpFlagsFilter::MatchOneAtLeast + */ + void setTcpFlagsBitMask(uint8_t tcpFlagBitMask, MatchOptions matchOption) { m_TcpFlagsBitMask = tcpFlagBitMask; m_MatchOption = matchOption; } + + void parseToString(std::string& result); + }; + + + + /** + * @class TcpWindowSizeFilter + * A class for filtering TCP packets that matches TCP window-size criteria
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class TcpWindowSizeFilter : public IFilterWithOperator + { + private: + uint16_t m_WindowSize; + public: + /** + * A constructor that get the window-size and operator and creates the filter. For example: "filter all TCP packets with window-size + * less than 1000" + * @param[in] windowSize The window-size value that will be used in the filter + * @param[in] op The operator to use (e.g "equal", "greater than", etc.) + */ + TcpWindowSizeFilter(uint16_t windowSize, FilterOperator op) : IFilterWithOperator(op), m_WindowSize(windowSize) {} + + void parseToString(std::string& result); + + /** + * 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; } + }; + + + + /** + * @class UdpLengthFilter + * A class for filtering UDP packets that matches UDP length criteria
+ * For deeper understanding of the filter concept please refer to PcapFilter.h + */ + class UdpLengthFilter : public IFilterWithOperator + { + private: + uint16_t m_Length; + public: + /** + * A constructor that get the UDP length and operator and creates the filter. For example: "filter all UDP packets with length + * greater or equal to 500" + * @param[in] length The length value that will be used in the filter + * @param[in] op The operator to use (e.g "equal", "greater than", etc.) + */ + UdpLengthFilter(uint16_t length, FilterOperator op) : IFilterWithOperator(op), m_Length(length) {} + + void parseToString(std::string& result); + + /** + * Set length value + * @param[in] length The length value that will be used in the filter + */ + void setLength(uint16_t length) { m_Length = length; } + }; + +} // namespace pcpp + +#endif diff --git a/Pcap++/header/PcapLiveDeviceList.h b/Pcap++/header/PcapLiveDeviceList.h index bd2bd871ea..237e9d9ec9 100644 --- a/Pcap++/header/PcapLiveDeviceList.h +++ b/Pcap++/header/PcapLiveDeviceList.h @@ -1,121 +1,121 @@ -#ifndef PCAPPP_LIVE_DEVICE_LIST -#define PCAPPP_LIVE_DEVICE_LIST - -#include "IpAddress.h" -#include "PcapLiveDevice.h" -#include - - -/// @file - -/** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @class PcapLiveDeviceList - * A singleton class that creates, stores and provides access to all PcapLiveDevice (on Linux) or WinPcapLiveDevice (on Windows) instances. All live - * devices are initialized on startup and wrap the network interfaces installed on the machine. This class enables access to them through - * their IP addresses or get a vector of all of them so the user can search them in some other way - */ - class PcapLiveDeviceList - { - private: - std::vector m_LiveDeviceList; - - std::vector m_DnsServers; - - // private c'tor - PcapLiveDeviceList(); - // private copy c'tor - PcapLiveDeviceList( const PcapLiveDeviceList& other ); - PcapLiveDeviceList& operator=(const PcapLiveDeviceList& other); - - void init(); - - void setDnsServers(); - public: - /** - * The access method to the singleton - * @return The singleton instance of this class - */ - static PcapLiveDeviceList& getInstance() - { - static PcapLiveDeviceList instance; - return instance; - } - - /** - * @return A vector containing pointers to all live devices currently installed on the machine - */ - const std::vector& getPcapLiveDevicesList() const { return m_LiveDeviceList; } - - /** - * Get a pointer to the live device by its IP address. IP address can be both IPv4 or IPv6 - * @param[in] ipAddr The IP address defined for the device - * @return A pointer to the live device if this IP address exists. NULL otherwise - */ - PcapLiveDevice* getPcapLiveDeviceByIp(const IPAddress& ipAddr) const; - - /** - * Get a pointer to the live device by its IPv4 address - * @param[in] ipAddr The IPv4 address defined for the device - * @return A pointer to the live device if this IPv4 address exists. NULL otherwise - */ - PcapLiveDevice* getPcapLiveDeviceByIp(const IPv4Address& ipAddr) const; - - /** - * Get a pointer to the live device by its IPv6 address - * @param[in] ip6Addr The IPv6 address defined for the device - * @return A pointer to the live device if this IPv6 address exists. NULL otherwise - */ - PcapLiveDevice* getPcapLiveDeviceByIp(const IPv6Address& ip6Addr) const; - - /** - * Get a pointer to the live device by its IP address represented as string. IP address can be both IPv4 or IPv6 - * @param[in] ipAddrAsString The IP address defined for the device as string - * @return A pointer to the live device if this IP address is valid and exists. NULL otherwise - */ - PcapLiveDevice* getPcapLiveDeviceByIp(const std::string& ipAddrAsString) const; - - /** - * Get a pointer to the live device by its name - * @param[in] name The name of the interface (e.g eth0) - * @return A pointer to the live device if this name exists. NULL otherwise - */ - PcapLiveDevice* getPcapLiveDeviceByName(const std::string& name) const; - - /** - * Get a pointer to the live device by its IP address or name - * @param[in] ipOrName An IP address or name of the interface - * @return A pointer to the live device if exists, NULL otherwise - */ - PcapLiveDevice* getPcapLiveDeviceByIpOrName(const std::string& ipOrName) const; - - /** - * @return A list of all DNS servers defined for this machine. If this list is empty it means no DNS servers were defined or they - * couldn't be extracted from some reason - */ - const std::vector& getDnsServers() const { return m_DnsServers; } - - /** - * Copies the current live device list - * @return A pointer to the cloned device list - */ - PcapLiveDeviceList* clone(); - - /** - * Reset the live device list and DNS server list, meaning clear and refetch them - */ - void reset(); - - // d'tor - ~PcapLiveDeviceList(); - }; - -} // namespace pcpp - -#endif +#ifndef PCAPPP_LIVE_DEVICE_LIST +#define PCAPPP_LIVE_DEVICE_LIST + +#include "IpAddress.h" +#include "PcapLiveDevice.h" +#include + + +/// @file + +/** +* \namespace pcpp +* \brief The main namespace for the PcapPlusPlus lib +*/ +namespace pcpp +{ + + /** + * @class PcapLiveDeviceList + * A singleton class that creates, stores and provides access to all PcapLiveDevice (on Linux) or WinPcapLiveDevice (on Windows) instances. All live + * devices are initialized on startup and wrap the network interfaces installed on the machine. This class enables access to them through + * their IP addresses or get a vector of all of them so the user can search them in some other way + */ + class PcapLiveDeviceList + { + private: + std::vector m_LiveDeviceList; + + std::vector m_DnsServers; + + // private c'tor + PcapLiveDeviceList(); + // private copy c'tor + PcapLiveDeviceList( const PcapLiveDeviceList& other ); + PcapLiveDeviceList& operator=(const PcapLiveDeviceList& other); + + void init(); + + void setDnsServers(); + public: + /** + * The access method to the singleton + * @return The singleton instance of this class + */ + static PcapLiveDeviceList& getInstance() + { + static PcapLiveDeviceList instance; + return instance; + } + + /** + * @return A vector containing pointers to all live devices currently installed on the machine + */ + const std::vector& getPcapLiveDevicesList() const { return m_LiveDeviceList; } + + /** + * Get a pointer to the live device by its IP address. IP address can be both IPv4 or IPv6 + * @param[in] ipAddr The IP address defined for the device + * @return A pointer to the live device if this IP address exists. NULL otherwise + */ + PcapLiveDevice* getPcapLiveDeviceByIp(const IPAddress& ipAddr) const; + + /** + * Get a pointer to the live device by its IPv4 address + * @param[in] ipAddr The IPv4 address defined for the device + * @return A pointer to the live device if this IPv4 address exists. NULL otherwise + */ + PcapLiveDevice* getPcapLiveDeviceByIp(const IPv4Address& ipAddr) const; + + /** + * Get a pointer to the live device by its IPv6 address + * @param[in] ip6Addr The IPv6 address defined for the device + * @return A pointer to the live device if this IPv6 address exists. NULL otherwise + */ + PcapLiveDevice* getPcapLiveDeviceByIp(const IPv6Address& ip6Addr) const; + + /** + * Get a pointer to the live device by its IP address represented as string. IP address can be both IPv4 or IPv6 + * @param[in] ipAddrAsString The IP address defined for the device as string + * @return A pointer to the live device if this IP address is valid and exists. NULL otherwise + */ + PcapLiveDevice* getPcapLiveDeviceByIp(const std::string& ipAddrAsString) const; + + /** + * Get a pointer to the live device by its name + * @param[in] name The name of the interface (e.g eth0) + * @return A pointer to the live device if this name exists. NULL otherwise + */ + PcapLiveDevice* getPcapLiveDeviceByName(const std::string& name) const; + + /** + * Get a pointer to the live device by its IP address or name + * @param[in] ipOrName An IP address or name of the interface + * @return A pointer to the live device if exists, NULL otherwise + */ + PcapLiveDevice* getPcapLiveDeviceByIpOrName(const std::string& ipOrName) const; + + /** + * @return A list of all DNS servers defined for this machine. If this list is empty it means no DNS servers were defined or they + * couldn't be extracted from some reason + */ + const std::vector& getDnsServers() const { return m_DnsServers; } + + /** + * Copies the current live device list + * @return A pointer to the cloned device list + */ + PcapLiveDeviceList* clone(); + + /** + * Reset the live device list and DNS server list, meaning clear and refetch them + */ + void reset(); + + // d'tor + ~PcapLiveDeviceList(); + }; + +} // namespace pcpp + +#endif diff --git a/Pcap++/header/PcapRemoteDevice.h b/Pcap++/header/PcapRemoteDevice.h index 47a81fbe65..7058fe66cf 100644 --- a/Pcap++/header/PcapRemoteDevice.h +++ b/Pcap++/header/PcapRemoteDevice.h @@ -1,147 +1,147 @@ -#ifndef PCAPPP_PCAP_REMOTE_DEVICE -#define PCAPPP_PCAP_REMOTE_DEVICE - -#if defined(_WIN32) - -#include -#include "PcapLiveDevice.h" - - -/// @file - -struct pcap_rmtauth; - -/** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @struct PcapRemoteAuthentication - * The remote daemon (rpcapd) can be configured to require authentication before allowing a client to connect. This is done for - * security reasons of course. This struct wraps the WinPcap/Npcap authentication object (pcap_rmtauth) and can (but not must) be given to - * PcapRemoteDeviceList when initiating a connection to the remote daemon - */ - struct PcapRemoteAuthentication - { - public: - /** - * A constructor that sets username and password - * @param[in] username The username for authentication with the remote daemon - * @param[in] password The password for authentication with the remote daemon - */ - PcapRemoteAuthentication(const std::string &username, const std::string &password) : userName(username) { this->password = password; } - - /** - * A copy c'tor for this object - * @param[in] other The object to copy from - */ - PcapRemoteAuthentication(const PcapRemoteAuthentication& other) : userName(other.userName), password(other.password) {} - - /** - * The username for authentication - */ - std::string userName; - - /** - * The password for authentication - */ - std::string password; - - /** - * A conversion method from PcapRemoteAuthentication to pcap_rmtauth. Note: the char* pointers of the returned pcap_rmtauth points - * to the same places in memory as PcapRemoteAuthentication::userName and PcapRemoteAuthentication::password so the user should avoid - * freeing this memory - * @return A pcap_rmtauth that is converted from this class - */ - pcap_rmtauth getPcapRmAuth() const; - }; - - /** - * @class PcapRemoteDevice - * A class that provides a C++ wrapper for WinPcap/Npcap Remote Capture feature. This feature allows to interact to a remote machine and capture - * packets that are being transmitted on the remote network interfaces. This requires a remote daemon (called rpcapd) which performs the - * capture and sends data back and the local client (represented by PcapRemoteDevice) that sends the appropriate commands and receives the - * captured data. You can read more about this feature in WinPcap Remote Capture manual: https://www.winpcap.org/docs/docs_412/html/group__remote.html
- * Since this feature is supported in WinPcap and Npcap only and not in libpcap, PcapRemoteDevice can only be used in Windows only.
- * This class provides a wrapper for the local client, meaning it assumes the daemon (rpcapd) is already running on the remote machine and it - * tries to connect to it and start receiving/sending packets from/to it. This class assumes rpcapd is in passive mode, meaning - * PcapRemoteDevice connects to the remote daemon, sends the appropriate commands to it, and starts capturing packets, rather than letting the - * daemon connect to the client by itself. Using PcapRemoteDevice is very similar to using the other live devices (PcapLiveDevice or - * WinPcapLiveDevice), meaning the API's are the same and the same logic is used (for example: capturing is done on a different thread, - * sending packets are done on the same thread, etc.). For the full API and explanations, please refer to PcapLiveDevice. The reason for the - * similar API is that WinPcap/Npcap's API is very similar between Remote Capture and local network interface capture. The things that are different - * are some are some implementation details, mainly in making the connection to the remote daemon, and the way the user can get the instance - * of PcapRemoteDevice. For more details on that please refer to PcapRemoteDeviceList - */ - class PcapRemoteDevice : public PcapLiveDevice - { - friend class PcapRemoteDeviceList; - private: - IPAddress m_RemoteMachineIpAddress; - uint16_t m_RemoteMachinePort; - PcapRemoteAuthentication* m_RemoteAuthentication; - - // c'tor is private, as only PcapRemoteDeviceList should create instances of it, and it'll create only one for every remote interface - PcapRemoteDevice(pcap_if_t* iface, PcapRemoteAuthentication* remoteAuthentication, const IPAddress& remoteMachineIP, uint16_t remoteMachinePort); - - // private copy c'tor - PcapRemoteDevice( const PcapRemoteDevice& other ); - // private assignment operator - PcapRemoteDevice& operator=(const PcapRemoteDevice& other); - - static void* remoteDeviceCaptureThreadMain(void *ptr); - - //overridden methods - ThreadStart getCaptureThreadStart(); - - public: - virtual ~PcapRemoteDevice() {} - - /** - * @return The IP address of the remote machine where packets are transmitted from the remote machine to the client machine - */ - IPAddress getRemoteMachineIpAddress() const { return m_RemoteMachineIpAddress; } - - /** - * @return The port of the remote machine where packets are transmitted from the remote machine to the client machine - */ - uint16_t getRemoteMachinePort() const { return m_RemoteMachinePort; } - - //overridden methods - - virtual LiveDeviceType getDeviceType() const { return RemoteDevice; } - - /** - * MTU isn't supported for remote devices - * @return 0 - */ - virtual uint32_t getMtu() const; - - /** - * MAC address isn't supported for remote devices - * @return MacAddress#Zero - */ - virtual MacAddress getMacAddress() const; - - /** - * Open the device using pcap_open. Opening the device makes the connection to the remote daemon (including authentication if needed - * and provided). If this methods succeeds it means the connection to the remote daemon succeeded and the device is ready for use. - * As in PcapLiveDevice, packet capturing won't start yet. For packet capturing the user should call startCapture(). This implies - * that calling this method is a must before calling startCapture() (otherwise startCapture() will fail with a "device not open" error). - * The remote daemon is asked to capture packets in promiscuous mode - * @return True if the device was opened successfully, false otherwise. When opening the device fails an error will be printed to log - * as well, including the WinPcap/Npcap error if exists - */ - virtual bool open(); - - virtual void getStatistics(IPcapDevice::PcapStats& stats) const; - }; - -} // namespace pcpp - -#endif // _WIN32 - -#endif /* PCAPPP_PCAP_REMOTE_DEVICE */ +#ifndef PCAPPP_PCAP_REMOTE_DEVICE +#define PCAPPP_PCAP_REMOTE_DEVICE + +#if defined(_WIN32) + +#include +#include "PcapLiveDevice.h" + + +/// @file + +struct pcap_rmtauth; + +/** +* \namespace pcpp +* \brief The main namespace for the PcapPlusPlus lib +*/ +namespace pcpp +{ + + /** + * @struct PcapRemoteAuthentication + * The remote daemon (rpcapd) can be configured to require authentication before allowing a client to connect. This is done for + * security reasons of course. This struct wraps the WinPcap/Npcap authentication object (pcap_rmtauth) and can (but not must) be given to + * PcapRemoteDeviceList when initiating a connection to the remote daemon + */ + struct PcapRemoteAuthentication + { + public: + /** + * A constructor that sets username and password + * @param[in] username The username for authentication with the remote daemon + * @param[in] password The password for authentication with the remote daemon + */ + PcapRemoteAuthentication(const std::string &username, const std::string &password) : userName(username) { this->password = password; } + + /** + * A copy c'tor for this object + * @param[in] other The object to copy from + */ + PcapRemoteAuthentication(const PcapRemoteAuthentication& other) : userName(other.userName), password(other.password) {} + + /** + * The username for authentication + */ + std::string userName; + + /** + * The password for authentication + */ + std::string password; + + /** + * A conversion method from PcapRemoteAuthentication to pcap_rmtauth. Note: the char* pointers of the returned pcap_rmtauth points + * to the same places in memory as PcapRemoteAuthentication::userName and PcapRemoteAuthentication::password so the user should avoid + * freeing this memory + * @return A pcap_rmtauth that is converted from this class + */ + pcap_rmtauth getPcapRmAuth() const; + }; + + /** + * @class PcapRemoteDevice + * A class that provides a C++ wrapper for WinPcap/Npcap Remote Capture feature. This feature allows to interact to a remote machine and capture + * packets that are being transmitted on the remote network interfaces. This requires a remote daemon (called rpcapd) which performs the + * capture and sends data back and the local client (represented by PcapRemoteDevice) that sends the appropriate commands and receives the + * captured data. You can read more about this feature in WinPcap Remote Capture manual: https://www.winpcap.org/docs/docs_412/html/group__remote.html
+ * Since this feature is supported in WinPcap and Npcap only and not in libpcap, PcapRemoteDevice can only be used in Windows only.
+ * This class provides a wrapper for the local client, meaning it assumes the daemon (rpcapd) is already running on the remote machine and it + * tries to connect to it and start receiving/sending packets from/to it. This class assumes rpcapd is in passive mode, meaning + * PcapRemoteDevice connects to the remote daemon, sends the appropriate commands to it, and starts capturing packets, rather than letting the + * daemon connect to the client by itself. Using PcapRemoteDevice is very similar to using the other live devices (PcapLiveDevice or + * WinPcapLiveDevice), meaning the API's are the same and the same logic is used (for example: capturing is done on a different thread, + * sending packets are done on the same thread, etc.). For the full API and explanations, please refer to PcapLiveDevice. The reason for the + * similar API is that WinPcap/Npcap's API is very similar between Remote Capture and local network interface capture. The things that are different + * are some are some implementation details, mainly in making the connection to the remote daemon, and the way the user can get the instance + * of PcapRemoteDevice. For more details on that please refer to PcapRemoteDeviceList + */ + class PcapRemoteDevice : public PcapLiveDevice + { + friend class PcapRemoteDeviceList; + private: + IPAddress m_RemoteMachineIpAddress; + uint16_t m_RemoteMachinePort; + PcapRemoteAuthentication* m_RemoteAuthentication; + + // c'tor is private, as only PcapRemoteDeviceList should create instances of it, and it'll create only one for every remote interface + PcapRemoteDevice(pcap_if_t* iface, PcapRemoteAuthentication* remoteAuthentication, const IPAddress& remoteMachineIP, uint16_t remoteMachinePort); + + // private copy c'tor + PcapRemoteDevice( const PcapRemoteDevice& other ); + // private assignment operator + PcapRemoteDevice& operator=(const PcapRemoteDevice& other); + + static void* remoteDeviceCaptureThreadMain(void *ptr); + + //overridden methods + ThreadStart getCaptureThreadStart(); + + public: + virtual ~PcapRemoteDevice() {} + + /** + * @return The IP address of the remote machine where packets are transmitted from the remote machine to the client machine + */ + IPAddress getRemoteMachineIpAddress() const { return m_RemoteMachineIpAddress; } + + /** + * @return The port of the remote machine where packets are transmitted from the remote machine to the client machine + */ + uint16_t getRemoteMachinePort() const { return m_RemoteMachinePort; } + + //overridden methods + + virtual LiveDeviceType getDeviceType() const { return RemoteDevice; } + + /** + * MTU isn't supported for remote devices + * @return 0 + */ + virtual uint32_t getMtu() const; + + /** + * MAC address isn't supported for remote devices + * @return MacAddress#Zero + */ + virtual MacAddress getMacAddress() const; + + /** + * Open the device using pcap_open. Opening the device makes the connection to the remote daemon (including authentication if needed + * and provided). If this methods succeeds it means the connection to the remote daemon succeeded and the device is ready for use. + * As in PcapLiveDevice, packet capturing won't start yet. For packet capturing the user should call startCapture(). This implies + * that calling this method is a must before calling startCapture() (otherwise startCapture() will fail with a "device not open" error). + * The remote daemon is asked to capture packets in promiscuous mode + * @return True if the device was opened successfully, false otherwise. When opening the device fails an error will be printed to log + * as well, including the WinPcap/Npcap error if exists + */ + virtual bool open(); + + virtual void getStatistics(IPcapDevice::PcapStats& stats) const; + }; + +} // namespace pcpp + +#endif // _WIN32 + +#endif /* PCAPPP_PCAP_REMOTE_DEVICE */ diff --git a/Pcap++/header/PcapRemoteDeviceList.h b/Pcap++/header/PcapRemoteDeviceList.h index c6235bd219..d5cee37496 100644 --- a/Pcap++/header/PcapRemoteDeviceList.h +++ b/Pcap++/header/PcapRemoteDeviceList.h @@ -1,152 +1,152 @@ -#ifndef PCAPP_PCAP_REMOTE_DEVICE_LIST -#define PCAPP_PCAP_REMOTE_DEVICE_LIST - -#if defined(_WIN32) - -#include "IpAddress.h" -#include "PcapRemoteDevice.h" - -/// @file - -/** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @class PcapRemoteDeviceList - * A class that creates, stores and provides access to all instances of PcapRemoteDevice for a certain remote machine. To get an instance - * of this class use one of the static methods of getRemoteDeviceList(). These methods creates a PcapRemoteDeviceList instance for the - * certain remote machine which holds a list of PcapRemoteDevice instances, one for each remote network interface. Note there is - * not a public constructor for this class, so the only way to get an instance of it is through getRemoteDeviceList(). After getting - * this object, this class provides ways to access the PcapRemoteDevice instances: either through IP address of the remote network interface or - * by iterating the PcapRemoteDevice instances (through the PcapRemoteDeviceList#RemoteDeviceListIterator iterator)
- * Since Remote Capture is supported in WinPcap and Npcap only, this class is available in Windows only - */ - class PcapRemoteDeviceList - { - private: - std::vector m_RemoteDeviceList; - IPAddress m_RemoteMachineIpAddress; - uint16_t m_RemoteMachinePort; - PcapRemoteAuthentication* m_RemoteAuthentication; - - // private c'tor. User should create the list via static methods PcapRemoteDeviceList::getRemoteDeviceList() - PcapRemoteDeviceList() : m_RemoteMachinePort(0), m_RemoteAuthentication(NULL) {} - // private copy c'tor - PcapRemoteDeviceList(const PcapRemoteDeviceList& other); - PcapRemoteDeviceList& operator=(const PcapRemoteDeviceList& other); - - void setRemoteMachineIpAddress(const IPAddress& ipAddress); - void setRemoteMachinePort(uint16_t port); - void setRemoteAuthentication(const PcapRemoteAuthentication* remoteAuth); - - public: - /** - * Iterator object that can be used for iterating all PcapRemoteDevice in list - */ - typedef typename std::vector::iterator RemoteDeviceListIterator; - - /** - * Const iterator object that can be used for iterating all PcapRemoteDevice in a constant list - */ - typedef typename std::vector::const_iterator ConstRemoteDeviceListIterator; - - ~PcapRemoteDeviceList(); - - /** - * A static method for creating a PcapRemoteDeviceList instance for a certain remote machine. This methods creates the instance, and also - * creates a list of PcapRemoteDevice instances stored in it, one for each remote network interface. Notice this method allocates - * the PcapRemoteDeviceList instance and returns a pointer to it. It's the user responsibility to free it when done using it
- * This method overload is for remote daemons which don't require authentication for accessing them. For daemons which do require authentication - * use the other method overload - * @param[in] ipAddress The IP address of the remote machine through which clients can connect to the rpcapd daemon - * @param[in] port The port of the remote machine through which clients can connect to the rpcapd daemon - * @return A pointer to the newly created PcapRemoteDeviceList, or NULL if (an appropriate error will be printed to log in each case): - * - IP address provided is NULL or not valid - * - WinPcap/Npcap encountered an error in creating the remote connection string - * - WinPcap/Npcap encountered an error connecting to the rpcapd daemon on the remote machine or retrieving devices on the remote machine - */ - static PcapRemoteDeviceList* getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port); - - /** - * An overload of the previous getRemoteDeviceList() method but with authentication support. This method is suitable for connecting to - * remote daemons which require authentication for accessing them - * @param[in] ipAddress The IP address of the remote machine through which clients can connect to the rpcapd daemon - * @param[in] port The port of the remote machine through which clients can connect to the rpcapd daemon - * @param[in] remoteAuth A pointer to the authentication object which contains the username and password for connecting to the remote - * daemon - * @return A pointer to the newly created PcapRemoteDeviceList, or NULL if (an appropriate error will be printed to log in each case): - * - IP address provided is NULL or not valid - * - WinPcap/Npcap encountered an error in creating the remote connection string - * - WinPcap/Npcap encountered an error connecting to the rpcapd daemon on the remote machine or retrieving devices on the remote machine - */ - static PcapRemoteDeviceList* getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port, PcapRemoteAuthentication* remoteAuth); - - /** - * @return The IP address of the remote machine - */ - IPAddress getRemoteMachineIpAddress() const { return m_RemoteMachineIpAddress; } - - /** - * @return The port of the remote machine where packets are transmitted from the remote machine to the client machine - */ - uint16_t getRemoteMachinePort() const { return m_RemoteMachinePort; } - - /** - * Search a PcapRemoteDevice in the list by its IPv4 address - * @param[in] ip4Addr The IPv4 address - * @return The PcapRemoteDevice if found, NULL otherwise - */ - PcapRemoteDevice* getRemoteDeviceByIP(const IPv4Address& ip4Addr) const; - - /** - * Search a PcapRemoteDevice in the list by its IPv6 address - * @param[in] ip6Addr The IPv6 address - * @return The PcapRemoteDevice if found, NULL otherwise - */ - PcapRemoteDevice* getRemoteDeviceByIP(const IPv6Address& ip6Addr) const; - - /** - * Search a PcapRemoteDevice in the list by its IP address (IPv4 or IPv6) - * @param[in] ipAddr The IP address - * @return The PcapRemoteDevice if found, NULL otherwise - */ - PcapRemoteDevice* getRemoteDeviceByIP(const IPAddress& ipAddr) const; - - /** - * Search a PcapRemoteDevice in the list by its IP address - * @param[in] ipAddrAsString The IP address in string format - * @return The PcapRemoteDevice if found, NULL otherwise - */ - PcapRemoteDevice* getRemoteDeviceByIP(const std::string& ipAddrAsString) const; - - /** - * @return An iterator object pointing to the first PcapRemoteDevice in list - */ - RemoteDeviceListIterator begin() { return m_RemoteDeviceList.begin(); } - - /** - * @return A const iterator object pointing to the first PcapRemoteDevice in list - */ - ConstRemoteDeviceListIterator begin() const { return m_RemoteDeviceList.begin(); } - - /** - * @return An iterator object pointing to the last PcapRemoteDevice in list - */ - RemoteDeviceListIterator end() { return m_RemoteDeviceList.end(); } - - /** - * @return A const iterator object pointing to the last PcapRemoteDevice in list - */ - ConstRemoteDeviceListIterator end() const { return m_RemoteDeviceList.end(); } - - }; - -} // namespace pcpp - -#endif // _WIN32 - -#endif /* PCAPP_PCAP_REMOTE_DEVICE_LIST */ +#ifndef PCAPP_PCAP_REMOTE_DEVICE_LIST +#define PCAPP_PCAP_REMOTE_DEVICE_LIST + +#if defined(_WIN32) + +#include "IpAddress.h" +#include "PcapRemoteDevice.h" + +/// @file + +/** +* \namespace pcpp +* \brief The main namespace for the PcapPlusPlus lib +*/ +namespace pcpp +{ + + /** + * @class PcapRemoteDeviceList + * A class that creates, stores and provides access to all instances of PcapRemoteDevice for a certain remote machine. To get an instance + * of this class use one of the static methods of getRemoteDeviceList(). These methods creates a PcapRemoteDeviceList instance for the + * certain remote machine which holds a list of PcapRemoteDevice instances, one for each remote network interface. Note there is + * not a public constructor for this class, so the only way to get an instance of it is through getRemoteDeviceList(). After getting + * this object, this class provides ways to access the PcapRemoteDevice instances: either through IP address of the remote network interface or + * by iterating the PcapRemoteDevice instances (through the PcapRemoteDeviceList#RemoteDeviceListIterator iterator)
+ * Since Remote Capture is supported in WinPcap and Npcap only, this class is available in Windows only + */ + class PcapRemoteDeviceList + { + private: + std::vector m_RemoteDeviceList; + IPAddress m_RemoteMachineIpAddress; + uint16_t m_RemoteMachinePort; + PcapRemoteAuthentication* m_RemoteAuthentication; + + // private c'tor. User should create the list via static methods PcapRemoteDeviceList::getRemoteDeviceList() + PcapRemoteDeviceList() : m_RemoteMachinePort(0), m_RemoteAuthentication(NULL) {} + // private copy c'tor + PcapRemoteDeviceList(const PcapRemoteDeviceList& other); + PcapRemoteDeviceList& operator=(const PcapRemoteDeviceList& other); + + void setRemoteMachineIpAddress(const IPAddress& ipAddress); + void setRemoteMachinePort(uint16_t port); + void setRemoteAuthentication(const PcapRemoteAuthentication* remoteAuth); + + public: + /** + * Iterator object that can be used for iterating all PcapRemoteDevice in list + */ + typedef typename std::vector::iterator RemoteDeviceListIterator; + + /** + * Const iterator object that can be used for iterating all PcapRemoteDevice in a constant list + */ + typedef typename std::vector::const_iterator ConstRemoteDeviceListIterator; + + ~PcapRemoteDeviceList(); + + /** + * A static method for creating a PcapRemoteDeviceList instance for a certain remote machine. This methods creates the instance, and also + * creates a list of PcapRemoteDevice instances stored in it, one for each remote network interface. Notice this method allocates + * the PcapRemoteDeviceList instance and returns a pointer to it. It's the user responsibility to free it when done using it
+ * This method overload is for remote daemons which don't require authentication for accessing them. For daemons which do require authentication + * use the other method overload + * @param[in] ipAddress The IP address of the remote machine through which clients can connect to the rpcapd daemon + * @param[in] port The port of the remote machine through which clients can connect to the rpcapd daemon + * @return A pointer to the newly created PcapRemoteDeviceList, or NULL if (an appropriate error will be printed to log in each case): + * - IP address provided is NULL or not valid + * - WinPcap/Npcap encountered an error in creating the remote connection string + * - WinPcap/Npcap encountered an error connecting to the rpcapd daemon on the remote machine or retrieving devices on the remote machine + */ + static PcapRemoteDeviceList* getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port); + + /** + * An overload of the previous getRemoteDeviceList() method but with authentication support. This method is suitable for connecting to + * remote daemons which require authentication for accessing them + * @param[in] ipAddress The IP address of the remote machine through which clients can connect to the rpcapd daemon + * @param[in] port The port of the remote machine through which clients can connect to the rpcapd daemon + * @param[in] remoteAuth A pointer to the authentication object which contains the username and password for connecting to the remote + * daemon + * @return A pointer to the newly created PcapRemoteDeviceList, or NULL if (an appropriate error will be printed to log in each case): + * - IP address provided is NULL or not valid + * - WinPcap/Npcap encountered an error in creating the remote connection string + * - WinPcap/Npcap encountered an error connecting to the rpcapd daemon on the remote machine or retrieving devices on the remote machine + */ + static PcapRemoteDeviceList* getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port, PcapRemoteAuthentication* remoteAuth); + + /** + * @return The IP address of the remote machine + */ + IPAddress getRemoteMachineIpAddress() const { return m_RemoteMachineIpAddress; } + + /** + * @return The port of the remote machine where packets are transmitted from the remote machine to the client machine + */ + uint16_t getRemoteMachinePort() const { return m_RemoteMachinePort; } + + /** + * Search a PcapRemoteDevice in the list by its IPv4 address + * @param[in] ip4Addr The IPv4 address + * @return The PcapRemoteDevice if found, NULL otherwise + */ + PcapRemoteDevice* getRemoteDeviceByIP(const IPv4Address& ip4Addr) const; + + /** + * Search a PcapRemoteDevice in the list by its IPv6 address + * @param[in] ip6Addr The IPv6 address + * @return The PcapRemoteDevice if found, NULL otherwise + */ + PcapRemoteDevice* getRemoteDeviceByIP(const IPv6Address& ip6Addr) const; + + /** + * Search a PcapRemoteDevice in the list by its IP address (IPv4 or IPv6) + * @param[in] ipAddr The IP address + * @return The PcapRemoteDevice if found, NULL otherwise + */ + PcapRemoteDevice* getRemoteDeviceByIP(const IPAddress& ipAddr) const; + + /** + * Search a PcapRemoteDevice in the list by its IP address + * @param[in] ipAddrAsString The IP address in string format + * @return The PcapRemoteDevice if found, NULL otherwise + */ + PcapRemoteDevice* getRemoteDeviceByIP(const std::string& ipAddrAsString) const; + + /** + * @return An iterator object pointing to the first PcapRemoteDevice in list + */ + RemoteDeviceListIterator begin() { return m_RemoteDeviceList.begin(); } + + /** + * @return A const iterator object pointing to the first PcapRemoteDevice in list + */ + ConstRemoteDeviceListIterator begin() const { return m_RemoteDeviceList.begin(); } + + /** + * @return An iterator object pointing to the last PcapRemoteDevice in list + */ + RemoteDeviceListIterator end() { return m_RemoteDeviceList.end(); } + + /** + * @return A const iterator object pointing to the last PcapRemoteDevice in list + */ + ConstRemoteDeviceListIterator end() const { return m_RemoteDeviceList.end(); } + + }; + +} // namespace pcpp + +#endif // _WIN32 + +#endif /* PCAPP_PCAP_REMOTE_DEVICE_LIST */ diff --git a/Pcap++/header/PfRingDevice.h b/Pcap++/header/PfRingDevice.h index 999a56a0f5..ce4f8471f7 100644 --- a/Pcap++/header/PfRingDevice.h +++ b/Pcap++/header/PfRingDevice.h @@ -1,349 +1,349 @@ -#ifndef PCAPPP_PF_RING_DEVICE -#define PCAPPP_PF_RING_DEVICE - -#include "Device.h" -#include "MacAddress.h" -#include "SystemUtils.h" -#include "Packet.h" -#include -#include - -/// @file - -// forward declaration of PF_RING structs -struct __pfring; -typedef struct __pfring pfring; - -/** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - class PfRingDevice; - - typedef void (*OnPfRingPacketsArriveCallback)(RawPacket* packets, uint32_t numOfPackets, uint8_t threadId, PfRingDevice* device, void* userCookie); - - - /** - * @class PfRingDevice - * A class representing a PF_RING port - */ - class PfRingDevice : public IDevice, public IFilterableDevice - { - friend class PfRingDeviceList; - private: - - struct CoreConfiguration - { - std::thread RxThread; - pfring* Channel; - bool IsInUse; - bool IsAffinitySet; - - CoreConfiguration(); - void clear(); - }; - - pfring** m_PfRingDescriptors; - uint8_t m_NumOfOpenedRxChannels; - std::string m_DeviceName; - int m_InterfaceIndex; - MacAddress m_MacAddress; - int m_DeviceMTU; - CoreConfiguration m_CoreConfiguration[MAX_NUM_OF_CORES]; - bool m_StopThread; - OnPfRingPacketsArriveCallback m_OnPacketsArriveCallback; - void* m_OnPacketsArriveUserCookie; - bool m_ReentrantMode; - bool m_HwClockEnabled; - bool m_IsFilterCurrentlySet; - - PfRingDevice(const char* deviceName); - - bool initCoreConfigurationByCoreMask(CoreMask coreMask); - void captureThreadMain(std::condition_variable* startCond, std::mutex* startMutex, const int* startState); - - int openSingleRxChannel(const char* deviceName, pfring** ring); - - bool getIsHwClockEnable() { setPfRingDeviceAttributes(); return m_HwClockEnabled; } - bool setPfRingDeviceClock(pfring* ring); - - void clearCoreConfiguration(); - int getCoresInUseCount() const; - - void setPfRingDeviceAttributes(); - - bool sendData(const uint8_t* packetData, int packetDataLength, bool flushTxQueues); - public: - - /** - * An enum representing the type of packet distribution between different RX channels - */ - enum ChannelDistribution - { - /** - * Packets are distributed between channels in a round-robin manner - */ - RoundRobin, - /** - * Packets are distributed between channels per flow (each flow goes for different channel) - */ - PerFlow - }; - - /** - * @struct PfRingStats - * A container for PfRingDevice statistics - */ - struct PfRingStats - { - /** Number of packets received */ - uint64_t recv; - /** Number of packets dropped */ - uint64_t drop; - }; - - /** - * A destructor for PfRingDevice class - */ - ~PfRingDevice(); - - /** - * Get the MAC address of the current device - * @return The MAC address of the current device - */ - MacAddress getMacAddress() { setPfRingDeviceAttributes(); return m_MacAddress; } - - /** - * Get PF_RING interface index of the current device - * @return PF_RING interface index of the current device - */ - int getInterfaceIndex() { setPfRingDeviceAttributes(); return m_InterfaceIndex; } - - /** - * Get MTU of the current device - * @return Upon success return the device MTU, 0 otherwise - */ - int getMtu() { setPfRingDeviceAttributes(); return m_DeviceMTU; } - - /** - * Return true if device supports hardware timestamping. If it does, this feature will be automatically set - * for this device. You can read more about this in PF_RING documentation - * @return True if device supports hardware timestamping, false otherwise - */ - bool isHwClockEnabledForDevice() { setPfRingDeviceAttributes(); return m_HwClockEnabled; } - - /** - * Gets the interface name (e.g eth0, eth1, etc.) - * @return The interface name - */ - std::string getDeviceName() const { return m_DeviceName; } - - - /** - * Start single-threaded capturing with callback. Works with open() or openSingleRxChannel(). - * @param[in] onPacketsArrive A callback to call whenever a packet arrives - * @param[in] onPacketsArriveUserCookie A cookie that will be delivered to onPacketsArrive callback on every packet - * @return True if this action succeeds, false otherwise - */ - bool startCaptureSingleThread(OnPfRingPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie); - - /** - * Start multi-threaded (multi-core) capturing with callback. Works with openMultiRxChannels(). - * This method will return an error if the number of opened channels is different than the number of threads/cores - * requested - * @param[in] onPacketsArrive A callback to call whenever a packet arrives - * @param[in] onPacketsArriveUserCookie A cookie that will be delivered to onPacketsArrive callback on every packet - * @param[in] coreMask The cores to be used as mask. For example: - * @return True if this action succeeds, false otherwise - */ - bool startCaptureMultiThread(OnPfRingPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie, CoreMask coreMask); - - /** - * Stops capturing packets (works will all type of startCapture*) - */ - void stopCapture(); - - - /** - * Opens a single RX channel (=RX queue) on this interface. All packets will be received on a single thread - * without core affinity. If the channel ID requested doesn't exist on this interface, the method will fail - * (return false) - * @param[in] channelId The requested channel ID - * @return True if this action succeeds, false otherwise - */ - bool openSingleRxChannel(uint8_t channelId); - - /** - * Opens a set of RX channels (=RX queues) on this interface, identified by their IDs. All packets will be received on a single thread - * without core affinity. If one of the channel IDs requested doesn't exist on this interface, the method will fail - * (return false) - * @param[in] channelIds An array of channel IDs - * @param[in] numOfChannelIds The channel ID array size - * @return True if this action succeeds, false otherwise - */ - bool openMultiRxChannels(const uint8_t* channelIds, int numOfChannelIds); - - /** - * Opens numOfRxChannelsToOpen RX channels. If numOfRxChannelsToOpen is larger than available RX queues for this - * interface than a number of RX channels will be opened on each RX queue. For example: if the user asks for 10 - * RX channels but the interface has only 4 RX queues, then 3 RX channels will be opened for RX-queue0 and RX-queue2, - * and 2 RX channels will be opened for RX-queue2 and RX-queue3. - * Packets will be distributed between different RX queues on per-flow manner, but within multiple RX channels in - * the same RX queue packet will be distributed according to distribution requested by "dist" - * @param[in] numOfRxChannelsToOpen Number of RX channels to open - * @param[in] dist Distribution method - * @return True if this action succeeds, false otherwise - */ - bool openMultiRxChannels(uint8_t numOfRxChannelsToOpen, ChannelDistribution dist); - - /** - * Gets the number of RX channels currently open. RX channels aren't necessary interface's RX queues - * because in some cases the user asks to open several channels on the same queue. For example: if the user uses - * openMultiRxChannels() and asks to open 8 channels but interface has only 4 RX queues, 2 channels will be - * opened for each RX queue - * @return Number of opened RX channels - */ - uint8_t getNumOfOpenedRxChannels() const { return m_NumOfOpenedRxChannels; } - - /** - * Gets the total number of RX channels (RX queues) this interface has - * @return The number of RX channels (queues) for this interface - */ - uint8_t getTotalNumOfRxChannels() const; - - /** - * Gets the core used in the current thread context - * @return The system core used in the current thread context - */ - SystemCore getCurrentCoreId() const; - - /** - * Get the statistics of a specific thread/core (=RX channel) - * @param[in] core The requested core - * @param[out] stats A reference for the stats object where the stats are written. Current values will be overridden - */ - void getThreadStatistics(SystemCore core, PfRingStats& stats) const; - - /** - * Get the statistics of the current thread/core (=RX channel) - * @param[out] stats A reference for the stats object where the stats are written. Current values will be overridden - */ - void getCurrentThreadStatistics(PfRingStats& stats) const; - - /** - * Get the statistics for the entire device. If more than 1 RX channel is opened, this method aggregates the stats - * of all channels - * @param[out] stats A reference for the stats object where the stats are written. Current values will be overridden - */ - void getStatistics(PfRingStats& stats) const; - - /** - * Return true if filter is currently set - * @return True if filter is currently set, false otherwise - */ - bool isFilterCurrentlySet() const; - - /** - * Send a raw packet. This packet must be fully specified (the MAC address up) - * and it will be transmitted as-is without any further manipulation. - * This method doesn't change or manipulate the data in any way (hence the "const" declaration). - * Note this method flushes the TX queues after the data is sent. So if you want to send several packets - * In the burst please use sendPackets() - * @param[in] rawPacket The raw packet to send - * @return True if raw packet was sent completely, false otherwise - */ - bool sendPacket(const RawPacket& rawPacket); - - /** - * Send raw data. This data must be a valid and fully specified packet (the MAC address up); - * it will be transmitted as-is without any further manipulation. - * This method doesn't change or manipulate the data in any way (hence the "const" declaration). - * Note this method flushes the TX queues after the data is sent. So if you want to send several packets - * in the burst please use sendPackets() - * @param[in] packetData The raw data to send - * @param[in] packetDataLength the length of packetData - * @return True if raw packet was sent completely, false otherwise - * - */ - bool sendPacket(const uint8_t* packetData, int packetDataLength); - - /** - * Send a packet. This packet must be fully specified (the MAC address up) - * and it will be transmitted as-is without any further manipulation. - * This method doesn't change or manipulate the data in any way (hence the "const" declaration). - * Note this method flushes the TX queues after the data is sent. So if you want to send several packets - * In the burst please use sendPackets() - * @param[in] packet The packet to send - * @return True if raw packet was sent completely, false otherwise - */ - bool sendPacket(const Packet& packet); - - /** - * Send raw packets. All raw packets must be fully specified (the MAC address up) - * and it will be transmitted as-is without any further manipulation. - * This method doesn't change or manipulate the raw packets data in any way (hence the "const" declaration). - * This method flushes the TX queues only when the last packet is sent - * @param[in] rawPacketsArr The RawPacket array - * @param[in] arrLength RawPacket array length - * @return Number of packets that were sent completely - */ - int sendPackets(const RawPacket* rawPacketsArr, int arrLength); - - /** - * Send packets. All packets must be fully specified (the MAC address up) - * and it will be transmitted as-is without any further manipulation. - * This method doesn't change or manipulate the packets data in any way (hence the "const" declaration). - * This method flushes the TX queues only when the last packet is sent - * @param[in] packetsArr An array of pointers to Packet objects - * @param[in] arrLength Packet pointers array length - * @return Number of packets that were sent completely - */ - int sendPackets(const Packet** packetsArr, int arrLength); - - /** - * Send all raw packets pointed by the RawPacketVector. All packets must be fully specified (the MAC address up) - * and it will be transmitted as-is without any further manipulation. - * This method doesn't change or manipulate the packets data in any way (hence the "const" declaration). - * This method flushes the TX queues only when the last packet is sent - * @param[in] rawPackets The raw packet vector - * @return Number of raw packets that were sent completely - */ - int sendPackets(const RawPacketVector& rawPackets); - - - // implement abstract methods - - - /** - * Opens the entire device (including all RX channels/queues on this interface). All packets will be received - * on a single thread without core affinity - * @return True if this action succeeds, false otherwise - */ - bool open(); - - /** - * Closes all RX channels currently opened in device - */ - void close(); - - using IFilterableDevice::setFilter; - - /** - * Sets a BPF filter to the device - * @param[in] filterAsString The BPF filter in string format - */ - bool setFilter(std::string filterAsString); - - /** - * Remove a filter if currently set - * @return True if filter was removed successfully or if no filter was set, false otherwise - */ - bool clearFilter(); - }; - -} // namespace pcpp - -#endif /* PCAPPP_PF_RING_DEVICE */ +#ifndef PCAPPP_PF_RING_DEVICE +#define PCAPPP_PF_RING_DEVICE + +#include "Device.h" +#include "MacAddress.h" +#include "SystemUtils.h" +#include "Packet.h" +#include +#include + +/// @file + +// forward declaration of PF_RING structs +struct __pfring; +typedef struct __pfring pfring; + +/** +* \namespace pcpp +* \brief The main namespace for the PcapPlusPlus lib +*/ +namespace pcpp +{ + + class PfRingDevice; + + typedef void (*OnPfRingPacketsArriveCallback)(RawPacket* packets, uint32_t numOfPackets, uint8_t threadId, PfRingDevice* device, void* userCookie); + + + /** + * @class PfRingDevice + * A class representing a PF_RING port + */ + class PfRingDevice : public IDevice, public IFilterableDevice + { + friend class PfRingDeviceList; + private: + + struct CoreConfiguration + { + std::thread RxThread; + pfring* Channel; + bool IsInUse; + bool IsAffinitySet; + + CoreConfiguration(); + void clear(); + }; + + pfring** m_PfRingDescriptors; + uint8_t m_NumOfOpenedRxChannels; + std::string m_DeviceName; + int m_InterfaceIndex; + MacAddress m_MacAddress; + int m_DeviceMTU; + CoreConfiguration m_CoreConfiguration[MAX_NUM_OF_CORES]; + bool m_StopThread; + OnPfRingPacketsArriveCallback m_OnPacketsArriveCallback; + void* m_OnPacketsArriveUserCookie; + bool m_ReentrantMode; + bool m_HwClockEnabled; + bool m_IsFilterCurrentlySet; + + PfRingDevice(const char* deviceName); + + bool initCoreConfigurationByCoreMask(CoreMask coreMask); + void captureThreadMain(std::condition_variable* startCond, std::mutex* startMutex, const int* startState); + + int openSingleRxChannel(const char* deviceName, pfring** ring); + + bool getIsHwClockEnable() { setPfRingDeviceAttributes(); return m_HwClockEnabled; } + bool setPfRingDeviceClock(pfring* ring); + + void clearCoreConfiguration(); + int getCoresInUseCount() const; + + void setPfRingDeviceAttributes(); + + bool sendData(const uint8_t* packetData, int packetDataLength, bool flushTxQueues); + public: + + /** + * An enum representing the type of packet distribution between different RX channels + */ + enum ChannelDistribution + { + /** + * Packets are distributed between channels in a round-robin manner + */ + RoundRobin, + /** + * Packets are distributed between channels per flow (each flow goes for different channel) + */ + PerFlow + }; + + /** + * @struct PfRingStats + * A container for PfRingDevice statistics + */ + struct PfRingStats + { + /** Number of packets received */ + uint64_t recv; + /** Number of packets dropped */ + uint64_t drop; + }; + + /** + * A destructor for PfRingDevice class + */ + ~PfRingDevice(); + + /** + * Get the MAC address of the current device + * @return The MAC address of the current device + */ + MacAddress getMacAddress() { setPfRingDeviceAttributes(); return m_MacAddress; } + + /** + * Get PF_RING interface index of the current device + * @return PF_RING interface index of the current device + */ + int getInterfaceIndex() { setPfRingDeviceAttributes(); return m_InterfaceIndex; } + + /** + * Get MTU of the current device + * @return Upon success return the device MTU, 0 otherwise + */ + int getMtu() { setPfRingDeviceAttributes(); return m_DeviceMTU; } + + /** + * Return true if device supports hardware timestamping. If it does, this feature will be automatically set + * for this device. You can read more about this in PF_RING documentation + * @return True if device supports hardware timestamping, false otherwise + */ + bool isHwClockEnabledForDevice() { setPfRingDeviceAttributes(); return m_HwClockEnabled; } + + /** + * Gets the interface name (e.g eth0, eth1, etc.) + * @return The interface name + */ + std::string getDeviceName() const { return m_DeviceName; } + + + /** + * Start single-threaded capturing with callback. Works with open() or openSingleRxChannel(). + * @param[in] onPacketsArrive A callback to call whenever a packet arrives + * @param[in] onPacketsArriveUserCookie A cookie that will be delivered to onPacketsArrive callback on every packet + * @return True if this action succeeds, false otherwise + */ + bool startCaptureSingleThread(OnPfRingPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie); + + /** + * Start multi-threaded (multi-core) capturing with callback. Works with openMultiRxChannels(). + * This method will return an error if the number of opened channels is different than the number of threads/cores + * requested + * @param[in] onPacketsArrive A callback to call whenever a packet arrives + * @param[in] onPacketsArriveUserCookie A cookie that will be delivered to onPacketsArrive callback on every packet + * @param[in] coreMask The cores to be used as mask. For example: + * @return True if this action succeeds, false otherwise + */ + bool startCaptureMultiThread(OnPfRingPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie, CoreMask coreMask); + + /** + * Stops capturing packets (works will all type of startCapture*) + */ + void stopCapture(); + + + /** + * Opens a single RX channel (=RX queue) on this interface. All packets will be received on a single thread + * without core affinity. If the channel ID requested doesn't exist on this interface, the method will fail + * (return false) + * @param[in] channelId The requested channel ID + * @return True if this action succeeds, false otherwise + */ + bool openSingleRxChannel(uint8_t channelId); + + /** + * Opens a set of RX channels (=RX queues) on this interface, identified by their IDs. All packets will be received on a single thread + * without core affinity. If one of the channel IDs requested doesn't exist on this interface, the method will fail + * (return false) + * @param[in] channelIds An array of channel IDs + * @param[in] numOfChannelIds The channel ID array size + * @return True if this action succeeds, false otherwise + */ + bool openMultiRxChannels(const uint8_t* channelIds, int numOfChannelIds); + + /** + * Opens numOfRxChannelsToOpen RX channels. If numOfRxChannelsToOpen is larger than available RX queues for this + * interface than a number of RX channels will be opened on each RX queue. For example: if the user asks for 10 + * RX channels but the interface has only 4 RX queues, then 3 RX channels will be opened for RX-queue0 and RX-queue2, + * and 2 RX channels will be opened for RX-queue2 and RX-queue3. + * Packets will be distributed between different RX queues on per-flow manner, but within multiple RX channels in + * the same RX queue packet will be distributed according to distribution requested by "dist" + * @param[in] numOfRxChannelsToOpen Number of RX channels to open + * @param[in] dist Distribution method + * @return True if this action succeeds, false otherwise + */ + bool openMultiRxChannels(uint8_t numOfRxChannelsToOpen, ChannelDistribution dist); + + /** + * Gets the number of RX channels currently open. RX channels aren't necessary interface's RX queues + * because in some cases the user asks to open several channels on the same queue. For example: if the user uses + * openMultiRxChannels() and asks to open 8 channels but interface has only 4 RX queues, 2 channels will be + * opened for each RX queue + * @return Number of opened RX channels + */ + uint8_t getNumOfOpenedRxChannels() const { return m_NumOfOpenedRxChannels; } + + /** + * Gets the total number of RX channels (RX queues) this interface has + * @return The number of RX channels (queues) for this interface + */ + uint8_t getTotalNumOfRxChannels() const; + + /** + * Gets the core used in the current thread context + * @return The system core used in the current thread context + */ + SystemCore getCurrentCoreId() const; + + /** + * Get the statistics of a specific thread/core (=RX channel) + * @param[in] core The requested core + * @param[out] stats A reference for the stats object where the stats are written. Current values will be overridden + */ + void getThreadStatistics(SystemCore core, PfRingStats& stats) const; + + /** + * Get the statistics of the current thread/core (=RX channel) + * @param[out] stats A reference for the stats object where the stats are written. Current values will be overridden + */ + void getCurrentThreadStatistics(PfRingStats& stats) const; + + /** + * Get the statistics for the entire device. If more than 1 RX channel is opened, this method aggregates the stats + * of all channels + * @param[out] stats A reference for the stats object where the stats are written. Current values will be overridden + */ + void getStatistics(PfRingStats& stats) const; + + /** + * Return true if filter is currently set + * @return True if filter is currently set, false otherwise + */ + bool isFilterCurrentlySet() const; + + /** + * Send a raw packet. This packet must be fully specified (the MAC address up) + * and it will be transmitted as-is without any further manipulation. + * This method doesn't change or manipulate the data in any way (hence the "const" declaration). + * Note this method flushes the TX queues after the data is sent. So if you want to send several packets + * In the burst please use sendPackets() + * @param[in] rawPacket The raw packet to send + * @return True if raw packet was sent completely, false otherwise + */ + bool sendPacket(const RawPacket& rawPacket); + + /** + * Send raw data. This data must be a valid and fully specified packet (the MAC address up); + * it will be transmitted as-is without any further manipulation. + * This method doesn't change or manipulate the data in any way (hence the "const" declaration). + * Note this method flushes the TX queues after the data is sent. So if you want to send several packets + * in the burst please use sendPackets() + * @param[in] packetData The raw data to send + * @param[in] packetDataLength the length of packetData + * @return True if raw packet was sent completely, false otherwise + * + */ + bool sendPacket(const uint8_t* packetData, int packetDataLength); + + /** + * Send a packet. This packet must be fully specified (the MAC address up) + * and it will be transmitted as-is without any further manipulation. + * This method doesn't change or manipulate the data in any way (hence the "const" declaration). + * Note this method flushes the TX queues after the data is sent. So if you want to send several packets + * In the burst please use sendPackets() + * @param[in] packet The packet to send + * @return True if raw packet was sent completely, false otherwise + */ + bool sendPacket(const Packet& packet); + + /** + * Send raw packets. All raw packets must be fully specified (the MAC address up) + * and it will be transmitted as-is without any further manipulation. + * This method doesn't change or manipulate the raw packets data in any way (hence the "const" declaration). + * This method flushes the TX queues only when the last packet is sent + * @param[in] rawPacketsArr The RawPacket array + * @param[in] arrLength RawPacket array length + * @return Number of packets that were sent completely + */ + int sendPackets(const RawPacket* rawPacketsArr, int arrLength); + + /** + * Send packets. All packets must be fully specified (the MAC address up) + * and it will be transmitted as-is without any further manipulation. + * This method doesn't change or manipulate the packets data in any way (hence the "const" declaration). + * This method flushes the TX queues only when the last packet is sent + * @param[in] packetsArr An array of pointers to Packet objects + * @param[in] arrLength Packet pointers array length + * @return Number of packets that were sent completely + */ + int sendPackets(const Packet** packetsArr, int arrLength); + + /** + * Send all raw packets pointed by the RawPacketVector. All packets must be fully specified (the MAC address up) + * and it will be transmitted as-is without any further manipulation. + * This method doesn't change or manipulate the packets data in any way (hence the "const" declaration). + * This method flushes the TX queues only when the last packet is sent + * @param[in] rawPackets The raw packet vector + * @return Number of raw packets that were sent completely + */ + int sendPackets(const RawPacketVector& rawPackets); + + + // implement abstract methods + + + /** + * Opens the entire device (including all RX channels/queues on this interface). All packets will be received + * on a single thread without core affinity + * @return True if this action succeeds, false otherwise + */ + bool open(); + + /** + * Closes all RX channels currently opened in device + */ + void close(); + + using IFilterableDevice::setFilter; + + /** + * Sets a BPF filter to the device + * @param[in] filterAsString The BPF filter in string format + */ + bool setFilter(std::string filterAsString); + + /** + * Remove a filter if currently set + * @return True if filter was removed successfully or if no filter was set, false otherwise + */ + bool clearFilter(); + }; + +} // namespace pcpp + +#endif /* PCAPPP_PF_RING_DEVICE */ diff --git a/Pcap++/header/PfRingDeviceList.h b/Pcap++/header/PfRingDeviceList.h index 4944f9bf33..f3335a5de1 100644 --- a/Pcap++/header/PfRingDeviceList.h +++ b/Pcap++/header/PfRingDeviceList.h @@ -1,67 +1,67 @@ -#ifndef PCAPPP_PF_RING_DEVICE_LIST -#define PCAPPP_PF_RING_DEVICE_LIST - -#include "PfRingDevice.h" - -/// @file - -/** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @class PfRingDeviceList - * A singleton class that holds all available PF_RING devices. Through this class the user can iterate all PF_RING devices or find a specific - * device by name - */ - class PfRingDeviceList - { - private: - std::vector m_PfRingDeviceList; - std::string m_PfRingVersion; - - PfRingDeviceList(); - // private copy c'tor - PfRingDeviceList(const PfRingDeviceList& other); - PfRingDeviceList& operator=(const PfRingDeviceList& other); - // private d'tor - ~PfRingDeviceList(); - - void calcPfRingVersion(void* ring); - public: - /** - * A static method that returns the singleton object for PfRingDeviceList - * @return PfRingDeviceList singleton - */ - static PfRingDeviceList& getInstance() - { - static PfRingDeviceList instance; - return instance; - } - - /** - * Return a list of all available PF_RING devices - * @return a list of all available PF_RING devices - */ - const std::vector& getPfRingDevicesList() const { return m_PfRingDeviceList; } - - /** - * Get a PF_RING device by name. The name is the Linux interface name which appears in ifconfig - * (e.g eth0, eth1, etc.) - * @return A pointer to the PF_RING device - */ - PfRingDevice* getPfRingDeviceByName(const std::string &devName) const; - - /** - * Get installed PF_RING version - * @return A string representing PF_RING version - */ - std::string getPfRingVersion() const { return m_PfRingVersion; } - }; - -} // namespace pcpp - -#endif /* PCAPPP_PF_RING_DEVICE_LIST */ +#ifndef PCAPPP_PF_RING_DEVICE_LIST +#define PCAPPP_PF_RING_DEVICE_LIST + +#include "PfRingDevice.h" + +/// @file + +/** +* \namespace pcpp +* \brief The main namespace for the PcapPlusPlus lib +*/ +namespace pcpp +{ + + /** + * @class PfRingDeviceList + * A singleton class that holds all available PF_RING devices. Through this class the user can iterate all PF_RING devices or find a specific + * device by name + */ + class PfRingDeviceList + { + private: + std::vector m_PfRingDeviceList; + std::string m_PfRingVersion; + + PfRingDeviceList(); + // private copy c'tor + PfRingDeviceList(const PfRingDeviceList& other); + PfRingDeviceList& operator=(const PfRingDeviceList& other); + // private d'tor + ~PfRingDeviceList(); + + void calcPfRingVersion(void* ring); + public: + /** + * A static method that returns the singleton object for PfRingDeviceList + * @return PfRingDeviceList singleton + */ + static PfRingDeviceList& getInstance() + { + static PfRingDeviceList instance; + return instance; + } + + /** + * Return a list of all available PF_RING devices + * @return a list of all available PF_RING devices + */ + const std::vector& getPfRingDevicesList() const { return m_PfRingDeviceList; } + + /** + * Get a PF_RING device by name. The name is the Linux interface name which appears in ifconfig + * (e.g eth0, eth1, etc.) + * @return A pointer to the PF_RING device + */ + PfRingDevice* getPfRingDeviceByName(const std::string &devName) const; + + /** + * Get installed PF_RING version + * @return A string representing PF_RING version + */ + std::string getPfRingVersion() const { return m_PfRingVersion; } + }; + +} // namespace pcpp + +#endif /* PCAPPP_PF_RING_DEVICE_LIST */ diff --git a/Pcap++/header/RawSocketDevice.h b/Pcap++/header/RawSocketDevice.h index 5d860c25db..e298a16888 100644 --- a/Pcap++/header/RawSocketDevice.h +++ b/Pcap++/header/RawSocketDevice.h @@ -1,164 +1,164 @@ -#ifndef PCAPPP_RAW_SOCKET_DEVICE -#define PCAPPP_RAW_SOCKET_DEVICE - -/// @file - -#include "IpAddress.h" -#include "Device.h" - -/** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - /** - * @class RawSocketDevice - * A class that wraps the raw socket functionality. A raw socket is a network socket that allows direct sending and receiving - * of IP packets without any protocol-specific transport layer formatting - * (taken from Wikipedia: https://en.wikipedia.org/wiki/Network_socket#Raw_socket). - * This wrapper class enables creation of a raw socket, binding it to a network interface, and then receiving and sending - * packets on it. Current implementation supports only Windows and Linux because other platforms provide poor support for raw - * sockets making them practically unusable. There are also major differences between Linux and Windows in raw socket - * implementation, let's mention some of the: - * - On Windows administrative privileges are required for raw sockets creation, meaning the process running the code - * has to have these privileges. In Linux 'sudo' is required - * - On Windows raw sockets are implemented in L3, meaning the L2 (Ethernet) layer is omitted by the socket and only L3 and - * up are visible to the user. On Linux raw sockets are implemented on L2, meaning all layers (including the Ethernet - * data) are visible to the user. - * - On Windows sending packets is not supported, a raw socket can only receive packets. On Linux both send and receive are - * supported - * - Linux doesn't require binding to a specific network interface for receiving packets, but it does require binding - * for sending packets. Windows requires binding for receiving packets. For the sake of keeping a unified and simple cross-platform interface - * this class requires binding for both Linux and Windows, on both send and receive - * - * More details about opening the raw socket, receiving and sending packets are explained in the corresponding class methods. - * Raw sockets are supported for both IPv4 and IPv6, so you can create and bind raw sockets to each of the two. - * Also, there is no limit on the number of sockets opened for a specific IP address or network interface, so you can - * create multiple instances of this class and bind all of them to the same interface and IP address. - */ - class RawSocketDevice : public IDevice - { - public: - - /** - * An enum for reporting packet receive results - */ - enum RecvPacketResult - { - /** Receive success */ - RecvSuccess = 0, - /** Receive timeout - timeout expired without any packets being captured */ - RecvTimeout = 1, - /** Receive would block - in non-blocking mode if there are no packets in the rx queue the receive method will return immediately with this return value */ - RecvWouldBlock = 2, - /** Receive error, usually will be followed by an error log */ - RecvError = 3 - }; - - /* - * A c'tor for this class. This c'tor doesn't create the raw socket, but rather initializes internal structures. The actual - * raw socket creation is done in the open() method. Each raw socket is bound to a network interface which means - * packets will be received and sent from only from this network interface only - * @param[in] interfaceIP The network interface IP to bind the raw socket to. It can be either an IPv4 or IPv6 address - * (both are supported in raw sockets) - */ - explicit RawSocketDevice(const IPAddress& interfaceIP); - - /** - * A d'tor for this class. It closes the raw socket if not previously closed by calling close() - */ - ~RawSocketDevice(); - - /** - * Receive a packet on the raw socket. This method has several modes of operation: - * - Blocking/non-blocking - in blocking mode the method will not return until a packet is received on the socket - * or until the timeout expires. In non-blocking mode it will return immediately and in case no packets are on the - * receive queue RawSocketDevice#RecvWouldBlock will be returned. Unless specified otherwise, the default value is - * blocking mode - * - Receive timeout - in blocking mode, the user can set a timeout to wait until a packet is received. If the timeout - * expires and no packets were received, the method will return RawSocketDevice#RecvTimeout. The default value is a - * negative value which means no timeout - * - * There is a slight difference on this method's behavior between Windows and Linux around how packets are received. - * On Linux the received packet contains all layers starting from the L2 (Ethernet). However on Windows raw socket are - * integrated in L3 level so the received packet contains only L3 (IP) layer and up. - * @param[out] rawPacket An empty packet instance where the received packet data will be written to - * @param[in] blocking Indicates whether to run in blocking or non-blocking mode. Default value is blocking - * @param[in] timeout When in blocking mode, specifies the timeout [in seconds] to wait for a packet. If timeout expired - * and no packets were captured the method will return RawSocketDevice#RecvTimeout. Zero or negative values mean no - * timeout. The default value is no timeout - * @return The method returns one on the following values: - * - RawSocketDevice#RecvSuccess is returned if a packet was received successfully - * - RawSocketDevice#RecvTimeout is returned if in blocking mode and timeout expired - * - RawSocketDevice#RecvWouldBlock is returned if in non-blocking mode and no packets were captured - * - RawSocketDevice#RecvError is returned if an error occurred such as device is not opened or the recv operation - * returned some error. A log message will be followed specifying the error and error code - */ - RecvPacketResult receivePacket(RawPacket& rawPacket, bool blocking = true, int timeout = -1); - - /** - * Receive packets into a packet vector for a certain amount of time. This method starts a timer and invokes the - * receivePacket() method in blocking mode repeatedly until the timeout expires. All packets received successfully are - * put into a packet vector - * @param[out] packetVec The packet vector to add the received packet to - * @param[in] timeout Timeout in seconds to receive packets on the raw socket - * @param[out] failedRecv Number of receive attempts that failed - * @return The number of packets received successfully - */ - int receivePackets(RawPacketVector& packetVec, int timeout, int& failedRecv); - - /** - * Send an Ethernet packet to the network. L2 protocols other than Ethernet are not supported in raw sockets. - * The entire packet is sent as is, including the original Ethernet and IP data. - * This method is only supported in Linux as Windows doesn't allow sending packets from raw sockets. Using - * it from other platforms will also return "false" with a corresponding error log message - * @param[in] rawPacket The packet to send - * @return True if packet was sent successfully or false if the socket is not open, if the packet is not Ethernet or - * if there was a failure sending the packet - */ - bool sendPacket(const RawPacket* rawPacket); - - /** - * Send a set of Ethernet packets to the network. L2 protocols other than Ethernet are not supported by raw sockets. - * The entire packet is sent as is, including the original Ethernet and IP data. - * This method is only supported in Linux as Windows doesn't allow sending packets from raw sockets. Using it from - * other platforms will return "false" with an appropriate error log message - * @param[in] packetVec The set of packets to send - * @return The number of packets sent successfully. For packets that weren't sent successfully there will be a - * corresponding error message printed to log - */ - int sendPackets(const RawPacketVector& packetVec); - - // overridden methods - - /** - * Open the device by creating a raw socket and binding it to the network interface specified in the c'tor - * @return True if device was opened successfully, false otherwise with a corresponding error log message - */ - virtual bool open(); - - /** - * Close the raw socket - */ - virtual void close(); - - private: - - enum SocketFamily - { - Ethernet = 0, - IPv4 = 1, - IPv6 = 2 - }; - - SocketFamily m_SockFamily; - void* m_Socket; - IPAddress m_InterfaceIP; - - RecvPacketResult getError(int& errorCode) const; - - }; -} - -#endif // PCAPPP_RAW_SOCKET_DEVICE +#ifndef PCAPPP_RAW_SOCKET_DEVICE +#define PCAPPP_RAW_SOCKET_DEVICE + +/// @file + +#include "IpAddress.h" +#include "Device.h" + +/** +* \namespace pcpp +* \brief The main namespace for the PcapPlusPlus lib +*/ +namespace pcpp +{ + /** + * @class RawSocketDevice + * A class that wraps the raw socket functionality. A raw socket is a network socket that allows direct sending and receiving + * of IP packets without any protocol-specific transport layer formatting + * (taken from Wikipedia: https://en.wikipedia.org/wiki/Network_socket#Raw_socket). + * This wrapper class enables creation of a raw socket, binding it to a network interface, and then receiving and sending + * packets on it. Current implementation supports only Windows and Linux because other platforms provide poor support for raw + * sockets making them practically unusable. There are also major differences between Linux and Windows in raw socket + * implementation, let's mention some of the: + * - On Windows administrative privileges are required for raw sockets creation, meaning the process running the code + * has to have these privileges. In Linux 'sudo' is required + * - On Windows raw sockets are implemented in L3, meaning the L2 (Ethernet) layer is omitted by the socket and only L3 and + * up are visible to the user. On Linux raw sockets are implemented on L2, meaning all layers (including the Ethernet + * data) are visible to the user. + * - On Windows sending packets is not supported, a raw socket can only receive packets. On Linux both send and receive are + * supported + * - Linux doesn't require binding to a specific network interface for receiving packets, but it does require binding + * for sending packets. Windows requires binding for receiving packets. For the sake of keeping a unified and simple cross-platform interface + * this class requires binding for both Linux and Windows, on both send and receive + * + * More details about opening the raw socket, receiving and sending packets are explained in the corresponding class methods. + * Raw sockets are supported for both IPv4 and IPv6, so you can create and bind raw sockets to each of the two. + * Also, there is no limit on the number of sockets opened for a specific IP address or network interface, so you can + * create multiple instances of this class and bind all of them to the same interface and IP address. + */ + class RawSocketDevice : public IDevice + { + public: + + /** + * An enum for reporting packet receive results + */ + enum RecvPacketResult + { + /** Receive success */ + RecvSuccess = 0, + /** Receive timeout - timeout expired without any packets being captured */ + RecvTimeout = 1, + /** Receive would block - in non-blocking mode if there are no packets in the rx queue the receive method will return immediately with this return value */ + RecvWouldBlock = 2, + /** Receive error, usually will be followed by an error log */ + RecvError = 3 + }; + + /* + * A c'tor for this class. This c'tor doesn't create the raw socket, but rather initializes internal structures. The actual + * raw socket creation is done in the open() method. Each raw socket is bound to a network interface which means + * packets will be received and sent from only from this network interface only + * @param[in] interfaceIP The network interface IP to bind the raw socket to. It can be either an IPv4 or IPv6 address + * (both are supported in raw sockets) + */ + explicit RawSocketDevice(const IPAddress& interfaceIP); + + /** + * A d'tor for this class. It closes the raw socket if not previously closed by calling close() + */ + ~RawSocketDevice(); + + /** + * Receive a packet on the raw socket. This method has several modes of operation: + * - Blocking/non-blocking - in blocking mode the method will not return until a packet is received on the socket + * or until the timeout expires. In non-blocking mode it will return immediately and in case no packets are on the + * receive queue RawSocketDevice#RecvWouldBlock will be returned. Unless specified otherwise, the default value is + * blocking mode + * - Receive timeout - in blocking mode, the user can set a timeout to wait until a packet is received. If the timeout + * expires and no packets were received, the method will return RawSocketDevice#RecvTimeout. The default value is a + * negative value which means no timeout + * + * There is a slight difference on this method's behavior between Windows and Linux around how packets are received. + * On Linux the received packet contains all layers starting from the L2 (Ethernet). However on Windows raw socket are + * integrated in L3 level so the received packet contains only L3 (IP) layer and up. + * @param[out] rawPacket An empty packet instance where the received packet data will be written to + * @param[in] blocking Indicates whether to run in blocking or non-blocking mode. Default value is blocking + * @param[in] timeout When in blocking mode, specifies the timeout [in seconds] to wait for a packet. If timeout expired + * and no packets were captured the method will return RawSocketDevice#RecvTimeout. Zero or negative values mean no + * timeout. The default value is no timeout + * @return The method returns one on the following values: + * - RawSocketDevice#RecvSuccess is returned if a packet was received successfully + * - RawSocketDevice#RecvTimeout is returned if in blocking mode and timeout expired + * - RawSocketDevice#RecvWouldBlock is returned if in non-blocking mode and no packets were captured + * - RawSocketDevice#RecvError is returned if an error occurred such as device is not opened or the recv operation + * returned some error. A log message will be followed specifying the error and error code + */ + RecvPacketResult receivePacket(RawPacket& rawPacket, bool blocking = true, int timeout = -1); + + /** + * Receive packets into a packet vector for a certain amount of time. This method starts a timer and invokes the + * receivePacket() method in blocking mode repeatedly until the timeout expires. All packets received successfully are + * put into a packet vector + * @param[out] packetVec The packet vector to add the received packet to + * @param[in] timeout Timeout in seconds to receive packets on the raw socket + * @param[out] failedRecv Number of receive attempts that failed + * @return The number of packets received successfully + */ + int receivePackets(RawPacketVector& packetVec, int timeout, int& failedRecv); + + /** + * Send an Ethernet packet to the network. L2 protocols other than Ethernet are not supported in raw sockets. + * The entire packet is sent as is, including the original Ethernet and IP data. + * This method is only supported in Linux as Windows doesn't allow sending packets from raw sockets. Using + * it from other platforms will also return "false" with a corresponding error log message + * @param[in] rawPacket The packet to send + * @return True if packet was sent successfully or false if the socket is not open, if the packet is not Ethernet or + * if there was a failure sending the packet + */ + bool sendPacket(const RawPacket* rawPacket); + + /** + * Send a set of Ethernet packets to the network. L2 protocols other than Ethernet are not supported by raw sockets. + * The entire packet is sent as is, including the original Ethernet and IP data. + * This method is only supported in Linux as Windows doesn't allow sending packets from raw sockets. Using it from + * other platforms will return "false" with an appropriate error log message + * @param[in] packetVec The set of packets to send + * @return The number of packets sent successfully. For packets that weren't sent successfully there will be a + * corresponding error message printed to log + */ + int sendPackets(const RawPacketVector& packetVec); + + // overridden methods + + /** + * Open the device by creating a raw socket and binding it to the network interface specified in the c'tor + * @return True if device was opened successfully, false otherwise with a corresponding error log message + */ + virtual bool open(); + + /** + * Close the raw socket + */ + virtual void close(); + + private: + + enum SocketFamily + { + Ethernet = 0, + IPv4 = 1, + IPv6 = 2 + }; + + SocketFamily m_SockFamily; + void* m_Socket; + IPAddress m_InterfaceIP; + + RecvPacketResult getError(int& errorCode) const; + + }; +} + +#endif // PCAPPP_RAW_SOCKET_DEVICE diff --git a/Pcap++/header/WinPcapLiveDevice.h b/Pcap++/header/WinPcapLiveDevice.h index 624f32149a..b050f87690 100644 --- a/Pcap++/header/WinPcapLiveDevice.h +++ b/Pcap++/header/WinPcapLiveDevice.h @@ -1,64 +1,64 @@ -#ifndef PCAPP_WINPCAP_LIVE_DEVICE -#define PCAPP_WINPCAP_LIVE_DEVICE - -#if defined(_WIN32) - -/// @file - -#include "PcapLiveDevice.h" - -/** -* \namespace pcpp -* \brief The main namespace for the PcapPlusPlus lib -*/ -namespace pcpp -{ - - /** - * @class WinPcapLiveDevice - * A class that wraps a Windows network interface (each of the interfaces listed in ipconfig). - * This class is almost similar in its capabilities to PcapLiveDevice (its parent class) with some small changes that mainly result from - * differences between libpcap and WinPcap/Npcap. Please see the reference for PcapLiveDevice for more details - */ - class WinPcapLiveDevice : public PcapLiveDevice - { - friend class PcapLiveDeviceList; - protected: - int m_MinAmountOfDataToCopyFromKernelToApplication; - - // c'tor is not public, there should be only one for every interface (created by PcapLiveDeviceList) - WinPcapLiveDevice(pcap_if_t* iface, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway); - // copy c'tor is not public - WinPcapLiveDevice( const WinPcapLiveDevice& other ); - WinPcapLiveDevice& operator=(const WinPcapLiveDevice& other); - - public: - virtual LiveDeviceType getDeviceType() const { return WinPcapDevice; } - - bool startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie, int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie); - bool startCapture(int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie); - bool startCapture(RawPacketVector& capturedPacketsVector) { return PcapLiveDevice::startCapture(capturedPacketsVector); } - - virtual int sendPackets(RawPacket* rawPacketsArr, int arrLength); - - /** - * WinPcap/Npcap have a feature (that doesn't exist in libpcap) to change the minimum amount of data in the kernel buffer that causes a read - * from the application to return (unless the timeout expires). Please see documentation for pcap_setmintocopy for more info. This method - * enables the user to change this size. Note the device must be open for this method to work - * @param[in] size The size to set in bytes - * @return True if set succeeded, false if the device is closed or if pcap_setmintocopy failed - */ - bool setMinAmountOfDataToCopyFromKernelToApplication(int size); - - /** - * @return The current amount of data in the kernel buffer that causes a read from the application to return (see also - * setMinAmountOfDataToCopyFromKernelToApplication()) - */ - int getMinAmountOfDataToCopyFromKernelToApplication() const { return m_MinAmountOfDataToCopyFromKernelToApplication; } - }; - -} // namespace pcpp - -#endif // _WIN32 - -#endif /* PCAPP_WINPCAP_LIVE_DEVICE */ +#ifndef PCAPP_WINPCAP_LIVE_DEVICE +#define PCAPP_WINPCAP_LIVE_DEVICE + +#if defined(_WIN32) + +/// @file + +#include "PcapLiveDevice.h" + +/** +* \namespace pcpp +* \brief The main namespace for the PcapPlusPlus lib +*/ +namespace pcpp +{ + + /** + * @class WinPcapLiveDevice + * A class that wraps a Windows network interface (each of the interfaces listed in ipconfig). + * This class is almost similar in its capabilities to PcapLiveDevice (its parent class) with some small changes that mainly result from + * differences between libpcap and WinPcap/Npcap. Please see the reference for PcapLiveDevice for more details + */ + class WinPcapLiveDevice : public PcapLiveDevice + { + friend class PcapLiveDeviceList; + protected: + int m_MinAmountOfDataToCopyFromKernelToApplication; + + // c'tor is not public, there should be only one for every interface (created by PcapLiveDeviceList) + WinPcapLiveDevice(pcap_if_t* iface, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway); + // copy c'tor is not public + WinPcapLiveDevice( const WinPcapLiveDevice& other ); + WinPcapLiveDevice& operator=(const WinPcapLiveDevice& other); + + public: + virtual LiveDeviceType getDeviceType() const { return WinPcapDevice; } + + bool startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie, int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie); + bool startCapture(int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie); + bool startCapture(RawPacketVector& capturedPacketsVector) { return PcapLiveDevice::startCapture(capturedPacketsVector); } + + virtual int sendPackets(RawPacket* rawPacketsArr, int arrLength); + + /** + * WinPcap/Npcap have a feature (that doesn't exist in libpcap) to change the minimum amount of data in the kernel buffer that causes a read + * from the application to return (unless the timeout expires). Please see documentation for pcap_setmintocopy for more info. This method + * enables the user to change this size. Note the device must be open for this method to work + * @param[in] size The size to set in bytes + * @return True if set succeeded, false if the device is closed or if pcap_setmintocopy failed + */ + bool setMinAmountOfDataToCopyFromKernelToApplication(int size); + + /** + * @return The current amount of data in the kernel buffer that causes a read from the application to return (see also + * setMinAmountOfDataToCopyFromKernelToApplication()) + */ + int getMinAmountOfDataToCopyFromKernelToApplication() const { return m_MinAmountOfDataToCopyFromKernelToApplication; } + }; + +} // namespace pcpp + +#endif // _WIN32 + +#endif /* PCAPP_WINPCAP_LIVE_DEVICE */ diff --git a/Pcap++/src/DpdkDevice.cpp b/Pcap++/src/DpdkDevice.cpp index 93522ff466..5914929bf1 100644 --- a/Pcap++/src/DpdkDevice.cpp +++ b/Pcap++/src/DpdkDevice.cpp @@ -1,1440 +1,1440 @@ -#ifdef USE_DPDK - -#define LOG_MODULE PcapLogModuleDpdkDevice - -#define __STDC_LIMIT_MACROS -#define __STDC_FORMAT_MACROS - -#include "DpdkDevice.h" -#include "DpdkDeviceList.h" -#include "Logger.h" -#include "rte_version.h" -#if (RTE_VER_YEAR > 17) || (RTE_VER_YEAR == 17 && RTE_VER_MONTH >= 11) -#include "rte_bus_pci.h" -#endif -#include "rte_pci.h" -#include "rte_config.h" -#include "rte_ethdev.h" -#include "rte_errno.h" -#include "rte_malloc.h" -#include "rte_cycles.h" -#include -#include -#include - -#define MAX_BURST_SIZE 64 - -#define MEMPOOL_CACHE_SIZE 256 - -#if (RTE_VER_YEAR < 21) || (RTE_VER_YEAR == 21 && RTE_VER_MONTH < 11) -#define GET_MASTER_CORE rte_get_master_lcore -#else -#define GET_MASTER_CORE rte_get_main_lcore -#endif - -namespace pcpp -{ - -/** - * ================ - * Class DpdkDevice - * ================ - */ - -#define DPDK_COFIG_HEADER_SPLIT 0 /**< Header Split disabled */ -#define DPDK_COFIG_SPLIT_HEADER_SIZE 0 -#define DPDK_COFIG_HW_IP_CHECKSUM 0 /**< IP checksum offload disabled */ -#define DPDK_COFIG_HW_VLAN_FILTER 0 /**< VLAN filtering disabled */ -#define DPDK_COFIG_JUMBO_FRAME 0 /**< Jumbo Frame Support disabled */ -#define DPDK_COFIG_HW_STRIP_CRC 0 /**< CRC stripped by hardware disabled */ -#if (RTE_VER_YEAR < 21) || (RTE_VER_YEAR == 21 && RTE_VER_MONTH < 11) -#define DPDK_CONFIG_MQ_MODE ETH_RSS -#else -#define DPDK_CONFIG_MQ_MODE RTE_ETH_MQ_RX_RSS -#endif - - -//RSS random key: -uint8_t DpdkDevice::m_RSSKey[40] = { - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, - 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, - }; - -DpdkDevice::DpdkDevice(int port, uint32_t mBufPoolSize) - : m_Id(port), m_MacAddress(MacAddress::Zero) -{ - std::ostringstream deviceNameStream; - deviceNameStream << "DPDK_" << m_Id; - m_DeviceName = deviceNameStream.str(); - -#if (RTE_VER_YEAR > 19) || (RTE_VER_YEAR == 19 && RTE_VER_MONTH >= 8) - struct rte_ether_addr etherAddr; -#else - struct ether_addr etherAddr; -#endif - rte_eth_macaddr_get((uint8_t) m_Id, ðerAddr); - m_MacAddress = MacAddress(etherAddr.addr_bytes[0], etherAddr.addr_bytes[1], - etherAddr.addr_bytes[2], etherAddr.addr_bytes[3], - etherAddr.addr_bytes[4], etherAddr.addr_bytes[5]); - - rte_eth_dev_get_mtu((uint8_t) m_Id, &m_DeviceMtu); - - char mBufMemPoolName[32]; - sprintf(mBufMemPoolName, "MBufMemPool%d", m_Id); - if (!initMemPool(m_MBufMempool, mBufMemPoolName, mBufPoolSize)) - { - PCPP_LOG_ERROR("Could not initialize mBuf mempool. Device not initialized"); - return; - } - - m_NumOfRxQueuesOpened = 0; - m_NumOfTxQueuesOpened = 0; - - setDeviceInfo(); - - memset(&m_PrevStats, 0 ,sizeof(m_PrevStats)); - - m_TxBuffers = NULL; - m_TxBufferLastDrainTsc = NULL; - - m_DeviceOpened = false; - m_WasOpened = false; - m_StopThread = true; -} - -DpdkDevice::~DpdkDevice() -{ - if (m_TxBuffers != NULL) - delete [] m_TxBuffers; - - if (m_TxBufferLastDrainTsc != NULL) - delete [] m_TxBufferLastDrainTsc; -} - -uint32_t DpdkDevice::getCurrentCoreId() const -{ - return rte_lcore_id(); -} - -bool DpdkDevice::setMtu(uint16_t newMtu) -{ - int res = rte_eth_dev_set_mtu(m_Id, newMtu); - if (res != 0) - { - PCPP_LOG_ERROR("Couldn't set device MTU. DPDK error: " << res); - return false; - } - - PCPP_LOG_DEBUG("Managed to set MTU from " << m_DeviceMtu << " to " << newMtu); - m_DeviceMtu = newMtu; - return true; -} - -bool DpdkDevice::openMultiQueues(uint16_t numOfRxQueuesToOpen, uint16_t numOfTxQueuesToOpen, const DpdkDeviceConfiguration& config) -{ - if (m_DeviceOpened) - { - PCPP_LOG_ERROR("Device already opened"); - return false; - } - - // There is a VMXNET3 limitation that when opening a device with a certain number of RX+TX queues - // it's impossible to close it and open it again with a larger number of RX+TX queues. So for this - // PMD I made a patch to open the device in the first time with maximum RX & TX queue, close it - // immediately and open it again with number of queues the user wanted to - if (!m_WasOpened && m_PMDType == PMD_VMXNET3) - { - m_WasOpened = true; - openMultiQueues(getTotalNumOfRxQueues(), getTotalNumOfTxQueues(), config); - close(); - } - - m_Config = config; - - if (!configurePort(numOfRxQueuesToOpen, numOfTxQueuesToOpen)) - { - m_DeviceOpened = false; - return false; - } - - clearCoreConfiguration(); - - if (!initQueues(numOfRxQueuesToOpen, numOfTxQueuesToOpen)) - return false; - - if (!startDevice()) - { - PCPP_LOG_ERROR("failed to start device " << m_Id); - m_DeviceOpened = false; - return false; - } - - m_NumOfRxQueuesOpened = numOfRxQueuesToOpen; - m_NumOfTxQueuesOpened = numOfTxQueuesToOpen; - - rte_eth_stats_reset(m_Id); - - m_DeviceOpened = true; - return m_DeviceOpened; -} - - -void DpdkDevice::close() -{ - if (!m_DeviceOpened) - { - PCPP_LOG_DEBUG("Trying to close device [" << m_DeviceName << "] but device is already closed"); - return; - } - stopCapture(); - clearCoreConfiguration(); - m_NumOfRxQueuesOpened = 0; - m_NumOfTxQueuesOpened = 0; - rte_eth_dev_stop(m_Id); - PCPP_LOG_DEBUG("Called rte_eth_dev_stop for device [" << m_DeviceName << "]"); - - if (m_TxBuffers != NULL) - { - delete [] m_TxBuffers; - m_TxBuffers = NULL; - } - - if (m_TxBufferLastDrainTsc != NULL) - { - delete [] m_TxBufferLastDrainTsc; - m_TxBufferLastDrainTsc = NULL; - } - - m_DeviceOpened = false; -} - - -bool DpdkDevice::configurePort(uint8_t numOfRxQueues, uint8_t numOfTxQueues) -{ - if (numOfRxQueues > getTotalNumOfRxQueues()) - { - PCPP_LOG_ERROR("Could not open more than " << getTotalNumOfRxQueues() << " RX queues"); - return false; - } - - if (numOfTxQueues > getTotalNumOfTxQueues()) - { - PCPP_LOG_ERROR("Could not open more than " << getTotalNumOfTxQueues() << " TX queues"); - return false; - } - - // if PMD doesn't support RSS, set RSS HF to 0 - if (getSupportedRssHashFunctions() == 0 && getConfiguredRssHashFunction() != 0) - { - PCPP_LOG_DEBUG("PMD '" << m_PMDName << "' doesn't support RSS, setting RSS hash functions to 0"); - m_Config.rssHashFunction = RSS_NONE; - } - - if (!isDeviceSupportRssHashFunction(getConfiguredRssHashFunction())) - { - PCPP_LOG_ERROR("PMD '" << m_PMDName << "' doesn't support the request RSS hash functions 0x" << std::hex << getConfiguredRssHashFunction()); - return false; - } - - // verify num of RX queues is power of 2 - bool isRxQueuePowerOfTwo = !(numOfRxQueues == 0) && !(numOfRxQueues & (numOfRxQueues - 1)); - if (!isRxQueuePowerOfTwo) - { - PCPP_LOG_ERROR("Num of RX queues must be power of 2 (because of DPDK limitation). Attempted to open device with " << numOfRxQueues << " RX queues"); - return false; - } - - struct rte_eth_conf portConf; - memset(&portConf,0,sizeof(rte_eth_conf)); - portConf.rxmode.split_hdr_size = DPDK_COFIG_SPLIT_HEADER_SIZE; -#if (RTE_VER_YEAR < 18) || (RTE_VER_YEAR == 18 && RTE_VER_MONTH < 8) - portConf.rxmode.header_split = DPDK_COFIG_HEADER_SPLIT; - portConf.rxmode.hw_ip_checksum = DPDK_COFIG_HW_IP_CHECKSUM; - portConf.rxmode.hw_vlan_filter = DPDK_COFIG_HW_VLAN_FILTER; - portConf.rxmode.jumbo_frame = DPDK_COFIG_JUMBO_FRAME; - portConf.rxmode.hw_strip_crc = DPDK_COFIG_HW_STRIP_CRC; -#endif - portConf.rxmode.mq_mode = DPDK_CONFIG_MQ_MODE; - portConf.rx_adv_conf.rss_conf.rss_key = m_Config.rssKey; - portConf.rx_adv_conf.rss_conf.rss_key_len = m_Config.rssKeyLength; - portConf.rx_adv_conf.rss_conf.rss_hf = convertRssHfToDpdkRssHf(getConfiguredRssHashFunction()); - - int res = rte_eth_dev_configure((uint8_t) m_Id, numOfRxQueues, numOfTxQueues, &portConf); - if (res < 0) - { - PCPP_LOG_ERROR("Failed to configure device [" << m_DeviceName << "]. error is: '" << rte_strerror(res) << "' [Error code: " << res << "]"); - return false; - } - - PCPP_LOG_DEBUG("Successfully called rte_eth_dev_configure for device [" << m_DeviceName << "] with " << numOfRxQueues << " RX queues and " << numOfTxQueues << " TX queues"); - - return true; -} - -bool DpdkDevice::initQueues(uint8_t numOfRxQueuesToInit, uint8_t numOfTxQueuesToInit) -{ - rte_eth_dev_info devInfo; - rte_eth_dev_info_get(m_Id, &devInfo); - if (numOfRxQueuesToInit > devInfo.max_rx_queues) - { - PCPP_LOG_ERROR("Num of RX queues requested for open [" << numOfRxQueuesToInit << "] is larger than RX queues available in NIC [" << devInfo.max_rx_queues << "]"); - return false; - } - - if (numOfTxQueuesToInit > devInfo.max_tx_queues) - { - PCPP_LOG_ERROR("Num of TX queues requested for open [" << numOfTxQueuesToInit << "] is larger than TX queues available in NIC [" << devInfo.max_tx_queues << "]"); - return false; - } - - for (uint8_t i = 0; i < numOfRxQueuesToInit; i++) - { - int ret = rte_eth_rx_queue_setup((uint8_t) m_Id, i, - m_Config.receiveDescriptorsNumber, 0, - NULL, m_MBufMempool); - - if (ret < 0) - { - PCPP_LOG_ERROR("Failed to init RX queue for device [" << m_DeviceName << "]. Error was: '" << rte_strerror(ret) << "' [Error code: " << ret << "]"); - return false; - } - } - - PCPP_LOG_DEBUG("Successfully initialized " << numOfRxQueuesToInit << " RX queues for device [" << m_DeviceName << "]"); - - for (uint8_t i = 0; i < numOfTxQueuesToInit; i++) - { - int ret = rte_eth_tx_queue_setup((uint8_t) m_Id, i, - m_Config.transmitDescriptorsNumber, - 0, NULL); - if (ret < 0) - { - PCPP_LOG_ERROR("Failed to init TX queue #" << i << " for port " << m_Id << ". Error was: '" << rte_strerror(ret) << "' [Error code: " << ret << "]"); - return false; - } - } - - if (m_TxBuffers != NULL) - delete [] m_TxBuffers; - - if (m_TxBufferLastDrainTsc != NULL) - delete [] m_TxBufferLastDrainTsc; - - m_TxBuffers = new rte_eth_dev_tx_buffer*[numOfTxQueuesToInit]; - m_TxBufferLastDrainTsc = new uint64_t[numOfTxQueuesToInit]; - memset(m_TxBufferLastDrainTsc, 0, sizeof(uint64_t)*numOfTxQueuesToInit); - - for (uint8_t i = 0; i < numOfTxQueuesToInit; i++) - { - m_TxBuffers[i] = (rte_eth_dev_tx_buffer*)rte_zmalloc_socket("tx_buffer", RTE_ETH_TX_BUFFER_SIZE(MAX_BURST_SIZE), 0, rte_eth_dev_socket_id(m_Id)); - - if (m_TxBuffers[i] == NULL) - { - PCPP_LOG_ERROR("Failed to allocate TX buffer for port " << m_Id << " TX queue " << (int)i); - return false; - } - - int res = rte_eth_tx_buffer_init(m_TxBuffers[i], MAX_BURST_SIZE); - - if (res != 0) - { - PCPP_LOG_ERROR("Failed to init TX buffer for port " << m_Id << " TX queue " << (int)i); - return false; - } - } - - m_TxBufferDrainTsc = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S * m_Config.flushTxBufferTimeout; - - memset(m_TxBufferLastDrainTsc, 0, sizeof(uint64_t)*numOfTxQueuesToInit); - - PCPP_LOG_DEBUG("Successfully initialized " << numOfTxQueuesToInit << " TX queues for device [" << m_DeviceName << "]"); - - return true; -} - - -bool DpdkDevice::initMemPool(struct rte_mempool*& memPool, const char* mempoolName, uint32_t mBufPoolSize) -{ - bool ret = false; - - // create mbuf pool - memPool = rte_pktmbuf_pool_create(mempoolName, mBufPoolSize, MEMPOOL_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); - if (memPool == NULL) - { - PCPP_LOG_ERROR("Failed to create packets memory pool for port " << m_Id << ", pool name: " << mempoolName << ". Error was: '" << rte_strerror(rte_errno) << "' [Error code: " << rte_errno << "]"); - } - else - { - PCPP_LOG_DEBUG("Successfully initialized packets pool of size [" << mBufPoolSize << "] for device [" << m_DeviceName << "]"); - ret = true; - } - return ret; -} - -bool DpdkDevice::startDevice() -{ - int ret = rte_eth_dev_start((uint8_t) m_Id); - if (ret < 0) - { - PCPP_LOG_ERROR("Failed to start device " << m_Id << ". Error is " << ret); - return false; - } - - LinkStatus status; - getLinkStatus(status); - if (Logger::getInstance().isDebugEnabled(PcapLogModuleDpdkDevice)) - { - std::string linkStatus = (status.linkUp ? "up" : "down"); - std::string linkDuplex = (status.linkDuplex == LinkStatus::FULL_DUPLEX ? "full-duplex" : "half-duplex"); - PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] : Link " << linkStatus << "; Speed: " << status.linkSpeedMbps << " Mbps; " << linkDuplex); - } - - rte_eth_promiscuous_enable((uint8_t) m_Id); - PCPP_LOG_DEBUG("Started device [" << m_DeviceName << "]"); - - return true; -} - - -void DpdkDevice::clearCoreConfiguration() -{ - for (int i = 0; i < MAX_NUM_OF_CORES; i++) - { - m_CoreConfiguration[i].IsCoreInUse = false; - } -} - -int DpdkDevice::getCoresInUseCount() const -{ - int res = 0; - for (int i = 0; i < MAX_NUM_OF_CORES; i++) - if (m_CoreConfiguration[i].IsCoreInUse) - res++; - - return res; -} - -void DpdkDevice::setDeviceInfo() -{ - rte_eth_dev_info portInfo; - rte_eth_dev_info_get(m_Id, &portInfo); - m_PMDName = std::string(portInfo.driver_name); - - if (m_PMDName == "eth_bond") - m_PMDType = PMD_BOND; - else if (m_PMDName == "rte_em_pmd") - m_PMDType = PMD_E1000EM; - else if (m_PMDName == "rte_igb_pmd") - m_PMDType = PMD_IGB; - else if (m_PMDName == "rte_igbvf_pmd") - m_PMDType = PMD_IGBVF; - else if (m_PMDName == "rte_enic_pmd") - m_PMDType = PMD_ENIC; - else if (m_PMDName == "rte_pmd_fm10k") - m_PMDType = PMD_FM10K; - else if (m_PMDName == "rte_i40e_pmd" || m_PMDName == "net_i40e") - m_PMDType = PMD_I40E; - else if (m_PMDName == "rte_i40evf_pmd") - m_PMDType = PMD_I40EVF; - else if (m_PMDName == "rte_ixgbe_pmd") - m_PMDType = PMD_IXGBE; - else if (m_PMDName == "rte_ixgbevf_pmd") - m_PMDType = PMD_IXGBEVF; - else if (m_PMDName == "librte_pmd_mlx4") - m_PMDType = PMD_MLX4; - else if (m_PMDName == "eth_null") - m_PMDType = PMD_NULL; - else if (m_PMDName == "eth_pcap") - m_PMDType = PMD_PCAP; - else if (m_PMDName == "eth_ring") - m_PMDType = PMD_RING; - else if (m_PMDName == "rte_virtio_pmd") - m_PMDType = PMD_VIRTIO; - else if (m_PMDName == "rte_vmxnet3_pmd") - m_PMDType = PMD_VMXNET3; - else if (m_PMDName == "eth_xenvirt") - m_PMDType = PMD_XENVIRT; - else - m_PMDType = PMD_UNKNOWN; - -#if (RTE_VER_YEAR < 18) || (RTE_VER_YEAR == 18 && RTE_VER_MONTH < 5) // before 18.05 - char pciName[30]; - #if (RTE_VER_YEAR > 17) || (RTE_VER_YEAR == 17 && RTE_VER_MONTH >= 11) // 17.11 - 18.02 - rte_pci_device_name(&(portInfo.pci_dev->addr), pciName, 30); - #else // 16.11 - 17.11 - rte_eal_pci_device_name(&(portInfo.pci_dev->addr), pciName, 30); - #endif - m_PciAddress = std::string(pciName); -#else // 18.05 forward - m_PciAddress = std::string(portInfo.device->name); -#endif - - PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] has " << portInfo.max_rx_queues << " RX queues"); - PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] has " << portInfo.max_tx_queues << " TX queues"); - - m_TotalAvailableRxQueues = portInfo.max_rx_queues; - m_TotalAvailableTxQueues = portInfo.max_tx_queues; -} - - -bool DpdkDevice::isVirtual() const -{ - switch (m_PMDType) - { - case PMD_IGBVF: - case PMD_I40EVF: - case PMD_IXGBEVF: - case PMD_PCAP: - case PMD_RING: - case PMD_VIRTIO: - case PMD_VMXNET3: - case PMD_XENVIRT: - return true; - default: - return false; - } -} - - -void DpdkDevice::getLinkStatus(LinkStatus& linkStatus) const -{ - struct rte_eth_link link; - rte_eth_link_get((uint8_t) m_Id, &link); - linkStatus.linkUp = link.link_status; - linkStatus.linkSpeedMbps = (unsigned) link.link_speed; - linkStatus.linkDuplex = (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? LinkStatus::FULL_DUPLEX : LinkStatus::HALF_DUPLEX; -} - - -bool DpdkDevice::initCoreConfigurationByCoreMask(CoreMask coreMask) -{ - int i = 0; - int numOfCores = getNumOfCores(); - clearCoreConfiguration(); - while ((coreMask != 0) && (i < numOfCores)) - { - if (coreMask & 1) - { - if (i == DpdkDeviceList::getInstance().getDpdkMasterCore().Id) - { - PCPP_LOG_ERROR("Core " << i << " is the master core, you can't use it for capturing threads"); - clearCoreConfiguration(); - return false; - } - - if (!rte_lcore_is_enabled(i)) - { - PCPP_LOG_ERROR("Trying to use core #" << i << " which isn't initialized by DPDK"); - clearCoreConfiguration(); - return false; - } - m_CoreConfiguration[i].IsCoreInUse = true; - } - - coreMask = coreMask >> 1; - i++; - } - - if (coreMask != 0) // this mean coreMask contains a core that doesn't exist - { - PCPP_LOG_ERROR("Trying to use a core [" << i << "] that doesn't exist while machine has " << numOfCores << " cores"); - clearCoreConfiguration(); - return false; - } - - return true; -} - - -bool DpdkDevice::startCaptureSingleThread(OnDpdkPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie) -{ - if (!m_StopThread) - { - PCPP_LOG_ERROR("Device already capturing. Cannot start 2 capture sessions at the same time"); - return false; - } - - if (m_NumOfRxQueuesOpened != 1) - { - PCPP_LOG_ERROR("Cannot start capturing on a single thread when more than 1 RX queue is opened"); - return false; - } - - PCPP_LOG_DEBUG("Trying to start capturing on a single thread for device [" << m_DeviceName << "]"); - - clearCoreConfiguration(); - - m_OnPacketsArriveCallback = onPacketsArrive; - m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; - - m_StopThread = false; - - for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) - { - if (coreId == (int)GET_MASTER_CORE() || !rte_lcore_is_enabled(coreId)) - continue; - - m_CoreConfiguration[coreId].IsCoreInUse = true; - m_CoreConfiguration[coreId].RxQueueId = 0; - - PCPP_LOG_DEBUG("Trying to start capturing on core " << coreId); - int err = rte_eal_remote_launch(dpdkCaptureThreadMain, (void*)this, coreId); - if (err != 0) - { - PCPP_LOG_ERROR("Cannot create capture thread for device '" << m_DeviceName << "'"); - m_CoreConfiguration[coreId].IsCoreInUse = false; - return false; - } - - PCPP_LOG_DEBUG("Capturing started for device [" << m_DeviceName << "]"); - return true; - } - - PCPP_LOG_ERROR("Could not find initialized core so capturing thread cannot be initialized"); - return false; -} - -bool DpdkDevice::startCaptureMultiThreads(OnDpdkPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie, CoreMask coreMask) -{ - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device not opened"); - return false; - } - - if (!initCoreConfigurationByCoreMask(coreMask)) - return false; - - if (m_NumOfRxQueuesOpened != getCoresInUseCount()) - { - PCPP_LOG_ERROR("Cannot use a different number of queues and cores. Opened " << m_NumOfRxQueuesOpened << " queues but set " << getCoresInUseCount() << " cores in core mask"); - clearCoreConfiguration(); - return false; - } - - m_StopThread = false; - int rxQueue = 0; - for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) - { - if (!m_CoreConfiguration[coreId].IsCoreInUse) - continue; - - // create a new thread - m_CoreConfiguration[coreId].RxQueueId = rxQueue++; - int err = rte_eal_remote_launch(dpdkCaptureThreadMain, (void*)this, coreId); - if (err != 0) - { - PCPP_LOG_ERROR("Cannot create capture thread #" << coreId << " for device '" << m_DeviceName << "': [" << strerror(err) << "]"); - m_CoreConfiguration[coreId].clear(); - return false; - } - } - - m_OnPacketsArriveCallback = onPacketsArrive; - m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; - - return true; -} - -void DpdkDevice::stopCapture() -{ - PCPP_LOG_DEBUG("Trying to stop capturing on device [" << m_DeviceName << "]"); - m_StopThread = true; - for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) - { - if (!m_CoreConfiguration[coreId].IsCoreInUse) - continue; - rte_eal_wait_lcore(coreId); - PCPP_LOG_DEBUG("Thread on core [" << coreId << "] stopped"); - } - - PCPP_LOG_DEBUG("All capturing threads stopped"); -} - -int DpdkDevice::dpdkCaptureThreadMain(void *ptr) -{ - DpdkDevice* pThis = (DpdkDevice*)ptr; - struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; - - if (pThis == NULL) - { - PCPP_LOG_ERROR("Failed to retrieve DPDK device in capture thread main loop"); - return 1; - } - - uint32_t coreId = pThis->getCurrentCoreId(); - PCPP_LOG_DEBUG("Starting capture thread " << coreId); - - int queueId = pThis->m_CoreConfiguration[coreId].RxQueueId; - - while (likely(!pThis->m_StopThread)) - { - uint32_t numOfPktsReceived = rte_eth_rx_burst(pThis->m_Id, queueId, mBufArray, MAX_BURST_SIZE); - - if (unlikely(numOfPktsReceived == 0)) - continue; - - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - - if (likely(pThis->m_OnPacketsArriveCallback != NULL)) - { - MBufRawPacket rawPackets[MAX_BURST_SIZE]; - for (uint32_t index = 0; index < numOfPktsReceived; ++index) - { - rawPackets[index].setMBuf(mBufArray[index], time); - } - - pThis->m_OnPacketsArriveCallback(rawPackets, numOfPktsReceived, coreId, pThis, pThis->m_OnPacketsArriveUserCookie); - } - } - - PCPP_LOG_DEBUG("Exiting capture thread " << coreId); - - return 0; -} - -#define nanosec_gap(begin, end) ((end.tv_sec - begin.tv_sec) * 1000000000.0 + (end.tv_nsec - begin.tv_nsec)) - -void DpdkDevice::getStatistics(DpdkDeviceStats& stats) const -{ - timespec timestamp; - clock_gettime(CLOCK_MONOTONIC, ×tamp); - struct rte_eth_stats rteStats; - rte_eth_stats_get(m_Id, &rteStats); - - double secsElapsed = (double)nanosec_gap(m_PrevStats.timestamp, timestamp) / 1000000000.0; - - stats.devId = m_Id; - stats.timestamp = timestamp; - stats.rxErroneousPackets = rteStats.ierrors; - stats.rxMbufAlocFailed = rteStats.rx_nombuf; - stats.rxPacketsDroppedByHW = rteStats.imissed; - stats.aggregatedRxStats.packets = rteStats.ipackets; - stats.aggregatedRxStats.bytes = rteStats.ibytes; - stats.aggregatedRxStats.packetsPerSec = (stats.aggregatedRxStats.packets - m_PrevStats.aggregatedRxStats.packets) / secsElapsed; - stats.aggregatedRxStats.bytesPerSec = (stats.aggregatedRxStats.bytes - m_PrevStats.aggregatedRxStats.bytes) / secsElapsed; - stats.aggregatedTxStats.packets = rteStats.opackets; - stats.aggregatedTxStats.bytes = rteStats.obytes; - stats.aggregatedTxStats.packetsPerSec = (stats.aggregatedTxStats.packets - m_PrevStats.aggregatedTxStats.packets) / secsElapsed; - stats.aggregatedTxStats.bytesPerSec = (stats.aggregatedTxStats.bytes - m_PrevStats.aggregatedTxStats.bytes) / secsElapsed; - - int numRxQs = std::min(DPDK_MAX_RX_QUEUES, RTE_ETHDEV_QUEUE_STAT_CNTRS); - int numTxQs = std::min(DPDK_MAX_TX_QUEUES, RTE_ETHDEV_QUEUE_STAT_CNTRS); - - for (int i = 0; i < numRxQs; i++) - { - stats.rxStats[i].packets = rteStats.q_ipackets[i]; - stats.rxStats[i].bytes = rteStats.q_ibytes[i]; - stats.rxStats[i].packetsPerSec = (stats.rxStats[i].packets - m_PrevStats.rxStats[i].packets) / secsElapsed; - stats.rxStats[i].bytesPerSec = (stats.rxStats[i].bytes - m_PrevStats.rxStats[i].bytes) / secsElapsed; - } - - for (int i = 0; i < numTxQs; i++) - { - stats.txStats[i].packets = rteStats.q_opackets[i]; - stats.txStats[i].bytes = rteStats.q_obytes[i]; - stats.txStats[i].packetsPerSec = (stats.txStats[i].packets - m_PrevStats.txStats[i].packets) / secsElapsed; - stats.txStats[i].bytesPerSec = (stats.txStats[i].bytes - m_PrevStats.txStats[i].bytes) / secsElapsed; - } - - //m_PrevStats = stats; - memcpy(&m_PrevStats, &stats, sizeof(m_PrevStats)); -} - -void DpdkDevice::clearStatistics() -{ - rte_eth_stats_reset(m_Id); - memset(&m_PrevStats, 0 ,sizeof(m_PrevStats)); -} - - -bool DpdkDevice::setFilter(GeneralFilter& filter) -{ - //TODO: I think DPDK supports filters - PCPP_LOG_ERROR("Filters aren't supported in DPDK device"); - return false; -} - -bool DpdkDevice::setFilter(std::string filterAsString) -{ - //TODO: I think DPDK supports filters - PCPP_LOG_ERROR("Filters aren't supported in DPDK device"); - return false; -} - -uint16_t DpdkDevice::receivePackets(MBufRawPacketVector& rawPacketsArr, uint16_t rxQueueId) const -{ - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device not opened"); - return 0; - } - - if (!m_StopThread) - { - PCPP_LOG_ERROR("DpdkDevice capture mode is currently running. Cannot receive packets in parallel"); - return 0; - } - - if (rxQueueId >= m_TotalAvailableRxQueues) - { - PCPP_LOG_ERROR("RX queue ID #" << rxQueueId << " not available for this device"); - return 0; - } - - struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; - uint32_t numOfPktsReceived = rte_eth_rx_burst(m_Id, rxQueueId, mBufArray, MAX_BURST_SIZE); - - //the following line trashes the log with many messages. Uncomment only if necessary - //PCPP_LOG_DEBUG("Captured %d packets", numOfPktsReceived); - - if (unlikely(!numOfPktsReceived)) - { - return 0; - } - - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - - for (uint32_t index = 0; index < numOfPktsReceived; ++index) - { - struct rte_mbuf* mBuf = mBufArray[index]; - MBufRawPacket* newRawPacket = new MBufRawPacket(); - newRawPacket->setMBuf(mBuf, time); - rawPacketsArr.pushBack(newRawPacket); - } - - return numOfPktsReceived; -} - -uint16_t DpdkDevice::receivePackets(MBufRawPacket** rawPacketsArr, uint16_t rawPacketArrLength, uint16_t rxQueueId) const -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("Device not opened"); - return 0; - } - - if (unlikely(!m_StopThread)) - { - PCPP_LOG_ERROR("DpdkDevice capture mode is currently running. Cannot receive packets in parallel"); - return 0; - } - - if (unlikely(rxQueueId >= m_TotalAvailableRxQueues)) - { - PCPP_LOG_ERROR("RX queue ID #" << rxQueueId << " not available for this device"); - return 0; - } - - if (unlikely(rawPacketsArr == NULL)) - { - PCPP_LOG_ERROR("Provided address of array to store packets is NULL"); - return 0; - } - - struct rte_mbuf* mBufArray[rawPacketArrLength]; - uint16_t packetsReceived = rte_eth_rx_burst(m_Id, rxQueueId, mBufArray, rawPacketArrLength); - - if (unlikely(!packetsReceived)) - { - return 0; - } - - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - - for (size_t index = 0; index < packetsReceived; ++index) - { - struct rte_mbuf* mBuf = mBufArray[index]; - if (rawPacketsArr[index] == NULL) - rawPacketsArr[index] = new MBufRawPacket(); - - rawPacketsArr[index]->setMBuf(mBuf, time); - } - - return packetsReceived; -} - -uint16_t DpdkDevice::receivePackets(Packet** packetsArr, uint16_t packetsArrLength, uint16_t rxQueueId) const -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("Device not opened"); - return 0; - } - - if (unlikely(!m_StopThread)) - { - PCPP_LOG_ERROR("DpdkDevice capture mode is currently running. Cannot receive packets in parallel"); - return 0; - } - - if (unlikely(rxQueueId >= m_TotalAvailableRxQueues)) - { - PCPP_LOG_ERROR("RX queue ID #" << rxQueueId << " not available for this device"); - return 0; - } - - struct rte_mbuf* mBufArray[packetsArrLength]; - uint16_t packetsReceived = rte_eth_rx_burst(m_Id, rxQueueId, mBufArray, packetsArrLength); - - if (unlikely(!packetsReceived)) - { - return 0; - } - - timespec time; - clock_gettime(CLOCK_REALTIME, &time); - - for (size_t index = 0; index < packetsReceived; ++index) - { - struct rte_mbuf* mBuf = mBufArray[index]; - MBufRawPacket* newRawPacket = new MBufRawPacket(); - newRawPacket->setMBuf(mBuf, time); - if (packetsArr[index] == NULL) - packetsArr[index] = new Packet(); - - packetsArr[index]->setRawPacket(newRawPacket, true); - } - - return packetsReceived; -} - -uint16_t DpdkDevice::flushTxBuffer(bool flushOnlyIfTimeoutExpired, uint16_t txQueueId) -{ - bool flush = true; - - if (flushOnlyIfTimeoutExpired) - { - uint64_t curTsc = rte_rdtsc(); - - if (curTsc - m_TxBufferLastDrainTsc[txQueueId] > m_TxBufferDrainTsc) - m_TxBufferLastDrainTsc[txQueueId] = curTsc; - else - flush = false; - } - - if (flush) - return rte_eth_tx_buffer_flush(m_Id, txQueueId, m_TxBuffers[txQueueId]); - - return 0; -} - -static rte_mbuf* getNextPacketFromMBufRawPacketArray(void* packetStorage, int index) -{ - MBufRawPacket** packetsArr = (MBufRawPacket**)packetStorage; - return packetsArr[index]->getMBuf(); -} - -static rte_mbuf* getNextPacketFromMBufArray(void* packetStorage, int index) -{ - rte_mbuf** mbufArr = (rte_mbuf**)packetStorage; - return mbufArr[index]; -} - -static rte_mbuf* getNextPacketFromMBufRawPacketVec(void* packetStorage, int index) -{ - MBufRawPacketVector* packetVec = (MBufRawPacketVector*)packetStorage; - return packetVec->at(index)->getMBuf(); -} - -static rte_mbuf* getNextPacketFromMBufRawPacket(void* packetStorage, int index) -{ - MBufRawPacket* mbufRawPacket = (MBufRawPacket*)packetStorage; - return mbufRawPacket->getMBuf(); -} - -uint16_t DpdkDevice::sendPacketsInner(uint16_t txQueueId, void* packetStorage, PacketIterator iter, int arrLength, bool useTxBuffer) -{ - if (unlikely(!m_DeviceOpened)) - { - PCPP_LOG_ERROR("Device '" << m_DeviceName << "' not opened!"); - return 0; - } - - if (unlikely(txQueueId >= m_NumOfTxQueuesOpened)) - { - PCPP_LOG_ERROR("TX queue " << txQueueId << " isn't opened in device"); - return 0; - } - - rte_mbuf* mBufArr[MAX_BURST_SIZE]; - - int packetIndex = 0; - int mBufArrIndex = 0; - uint16_t packetsSent = 0; - int lastSleep = 0; - - #define PACKET_TRANSMISSION_THRESHOLD 0.8 - int packetTxThreshold = m_Config.transmitDescriptorsNumber*PACKET_TRANSMISSION_THRESHOLD; - - while (packetIndex < arrLength) - { - rte_mbuf* mBuf = iter(packetStorage, packetIndex); - - if (useTxBuffer) - { - packetsSent += rte_eth_tx_buffer(m_Id, txQueueId, m_TxBuffers[txQueueId], mBuf); - } - else - { - mBufArr[mBufArrIndex++] = mBuf; - - if (unlikely(mBufArrIndex == MAX_BURST_SIZE)) - { - packetsSent += rte_eth_tx_burst(m_Id, txQueueId, mBufArr, MAX_BURST_SIZE); - mBufArrIndex = 0; - - if (unlikely((packetsSent - lastSleep) >= packetTxThreshold)) - { - PCPP_LOG_DEBUG("Since NIC couldn't send all packet in this iteration, waiting for 0.2 second for H/W descriptors to get free"); - usleep(200000); - lastSleep = packetsSent; - } - } - } - - packetIndex++; - } - - if (useTxBuffer) - { - packetsSent += flushTxBuffer(true, txQueueId); - } - else if (mBufArrIndex > 0) - { - packetsSent += rte_eth_tx_burst(m_Id, txQueueId, mBufArr, mBufArrIndex); - } - - return packetsSent; -} - - -uint16_t DpdkDevice::sendPackets(MBufRawPacket** rawPacketsArr, uint16_t arrLength, uint16_t txQueueId, bool useTxBuffer) -{ - uint16_t packetsSent = sendPacketsInner(txQueueId, (void*)rawPacketsArr, getNextPacketFromMBufRawPacketArray, arrLength, useTxBuffer); - - bool needToFreeMbuf = false; - int applyForMBufs = arrLength; - - if (unlikely(!useTxBuffer && (packetsSent != arrLength))) - { - applyForMBufs = packetsSent; - } - - for (int index = 0; index < applyForMBufs; index++) - rawPacketsArr[index]->setFreeMbuf(needToFreeMbuf); - - for (int index = applyForMBufs; index < arrLength; index++) - rawPacketsArr[index]->setFreeMbuf(!needToFreeMbuf); - - return packetsSent; -} - -uint16_t DpdkDevice::sendPackets(Packet** packetsArr, uint16_t arrLength, uint16_t txQueueId, bool useTxBuffer) -{ - rte_mbuf* mBufArr[arrLength]; - MBufRawPacketVector mBufVec; - MBufRawPacket* mBufRawPacketArr[arrLength]; - - for (size_t i = 0; i < arrLength; i++) - { - MBufRawPacket* rawPacket = NULL; - uint8_t rawPacketType = packetsArr[i]->getRawPacketReadOnly()->getObjectType(); - if (rawPacketType != MBUFRAWPACKET_OBJECT_TYPE) - { - rawPacket = new MBufRawPacket(); - if (unlikely(!rawPacket->initFromRawPacket(packetsArr[i]->getRawPacketReadOnly(), this))) - { - delete rawPacket; - return 0; - } - - mBufVec.pushBack(rawPacket); - } - else - { - rawPacket = (MBufRawPacket*)packetsArr[i]->getRawPacketReadOnly(); - } - - mBufArr[i] = rawPacket->getMBuf(); - mBufRawPacketArr[i] = rawPacket; - } - - uint16_t packetsSent = sendPacketsInner(txQueueId, (void*)mBufArr, getNextPacketFromMBufArray, arrLength, useTxBuffer); - - bool needToFreeMbuf = (!useTxBuffer && (packetsSent != arrLength)); - for (int index = 0; index < arrLength; index++) - mBufRawPacketArr[index]->setFreeMbuf(needToFreeMbuf); - - return packetsSent; -} - -uint16_t DpdkDevice::sendPackets(RawPacketVector& rawPacketsVec, uint16_t txQueueId, bool useTxBuffer) -{ - size_t vecSize = rawPacketsVec.size(); - rte_mbuf* mBufArr[vecSize]; - MBufRawPacket* mBufRawPacketArr[vecSize]; - MBufRawPacketVector mBufVec; - int mBufIndex = 0; - - for (RawPacketVector::ConstVectorIterator iter = rawPacketsVec.begin(); iter != rawPacketsVec.end(); iter++) - { - MBufRawPacket* rawPacket = NULL; - uint8_t rawPacketType = (*iter)->getObjectType(); - if (rawPacketType != MBUFRAWPACKET_OBJECT_TYPE) - { - rawPacket = new MBufRawPacket(); - if (unlikely(!rawPacket->initFromRawPacket(*iter, this))) - { - delete rawPacket; - return 0; - } - - mBufVec.pushBack(rawPacket); - } - else - { - rawPacket = (MBufRawPacket*)(*iter); - } - - mBufRawPacketArr[mBufIndex] = rawPacket; - mBufArr[mBufIndex++] = rawPacket->getMBuf(); - } - - uint16_t packetsSent = sendPacketsInner(txQueueId, (void*)mBufArr, getNextPacketFromMBufArray, vecSize, useTxBuffer); - - bool needToFreeMbuf = (!useTxBuffer && (packetsSent != vecSize)); - for (size_t index = 0; index < rawPacketsVec.size(); index++) - mBufRawPacketArr[index]->setFreeMbuf(needToFreeMbuf); - - return packetsSent; -} - -uint16_t DpdkDevice::sendPackets(MBufRawPacketVector& rawPacketsVec, uint16_t txQueueId, bool useTxBuffer) -{ - size_t vecSize = rawPacketsVec.size(); - uint16_t packetsSent = sendPacketsInner(txQueueId, (void*)(&rawPacketsVec), getNextPacketFromMBufRawPacketVec, vecSize, useTxBuffer); - - bool needToFreeMbuf = (!useTxBuffer && (packetsSent != vecSize)); - - for (size_t index = 0; index < vecSize; index++) - rawPacketsVec.at(index)->setFreeMbuf(needToFreeMbuf); - - return packetsSent; -} - -bool DpdkDevice::sendPacket(RawPacket& rawPacket, uint16_t txQueueId, bool useTxBuffer) -{ - uint8_t rawPacketType = rawPacket.getObjectType(); - if (rawPacketType == MBUFRAWPACKET_OBJECT_TYPE) - { - bool packetSent = (sendPacketsInner(txQueueId, (MBufRawPacket*)&rawPacket, getNextPacketFromMBufRawPacket, 1, useTxBuffer) == 1); - bool needToFreeMbuf = (!useTxBuffer && !packetSent); - ((MBufRawPacket*)&rawPacket)->setFreeMbuf(needToFreeMbuf); - return packetSent; - } - - MBufRawPacket mbufRawPacket; - if (unlikely(!mbufRawPacket.initFromRawPacket(&rawPacket, this))) - return false; - - bool packetSent = (sendPacketsInner(txQueueId, &mbufRawPacket, getNextPacketFromMBufRawPacket, 1, useTxBuffer) == 1); - bool needToFreeMbuf = (!useTxBuffer && !packetSent); - mbufRawPacket.setFreeMbuf(needToFreeMbuf); - - return packetSent; -} - -bool DpdkDevice::sendPacket(MBufRawPacket& rawPacket, uint16_t txQueueId, bool useTxBuffer) -{ - bool packetSent = (sendPacketsInner(txQueueId, &rawPacket, getNextPacketFromMBufRawPacket, 1, useTxBuffer) == 1); - bool needToFreeMbuf = (!useTxBuffer && !packetSent); - rawPacket.setFreeMbuf(needToFreeMbuf); - return packetSent; -} - -bool DpdkDevice::sendPacket(Packet& packet, uint16_t txQueueId, bool useTxBuffer) -{ - return sendPacket(*(packet.getRawPacket()), txQueueId); -} - -int DpdkDevice::getAmountOfFreeMbufs() const -{ - return (int)rte_mempool_avail_count(m_MBufMempool); -} - -int DpdkDevice::getAmountOfMbufsInUse() const -{ - return (int)rte_mempool_in_use_count(m_MBufMempool); -} - -uint64_t DpdkDevice::convertRssHfToDpdkRssHf(uint64_t rssHF) const -{ - if (rssHF == (uint64_t)-1) - { - rte_eth_dev_info devInfo; - rte_eth_dev_info_get(m_Id, &devInfo); - return devInfo.flow_type_rss_offloads; - } - - uint64_t dpdkRssHF = 0; - - if ((rssHF & RSS_IPV4) != 0) - dpdkRssHF |= ETH_RSS_IPV4; - - if ((rssHF & RSS_FRAG_IPV4) != 0) - dpdkRssHF |= ETH_RSS_FRAG_IPV4; - - if ((rssHF & RSS_NONFRAG_IPV4_TCP) != 0) - dpdkRssHF |= ETH_RSS_NONFRAG_IPV4_TCP; - - if ((rssHF & RSS_NONFRAG_IPV4_UDP) != 0) - dpdkRssHF |= ETH_RSS_NONFRAG_IPV4_UDP; - - if ((rssHF & RSS_NONFRAG_IPV4_SCTP) != 0) - dpdkRssHF |= ETH_RSS_NONFRAG_IPV4_SCTP; - - if ((rssHF & RSS_NONFRAG_IPV4_OTHER) != 0) - dpdkRssHF |= ETH_RSS_NONFRAG_IPV4_OTHER; - - if ((rssHF & RSS_IPV6) != 0) - dpdkRssHF |= ETH_RSS_IPV6; - - if ((rssHF & RSS_FRAG_IPV6) != 0) - dpdkRssHF |= ETH_RSS_FRAG_IPV6; - - if ((rssHF & RSS_NONFRAG_IPV6_TCP) != 0) - dpdkRssHF |= ETH_RSS_NONFRAG_IPV6_TCP; - - if ((rssHF & RSS_NONFRAG_IPV6_UDP) != 0) - dpdkRssHF |= ETH_RSS_NONFRAG_IPV6_UDP; - - if ((rssHF & RSS_NONFRAG_IPV6_SCTP) != 0) - dpdkRssHF |= ETH_RSS_NONFRAG_IPV6_SCTP; - - if ((rssHF & RSS_NONFRAG_IPV6_OTHER) != 0) - dpdkRssHF |= ETH_RSS_NONFRAG_IPV6_OTHER; - - if ((rssHF & RSS_L2_PAYLOAD) != 0) - dpdkRssHF |= ETH_RSS_L2_PAYLOAD; - - if ((rssHF & RSS_IPV6_EX) != 0) - dpdkRssHF |= ETH_RSS_IPV6_EX; - - if ((rssHF & RSS_IPV6_TCP_EX) != 0) - dpdkRssHF |= ETH_RSS_IPV6_TCP_EX; - - if ((rssHF & RSS_IPV6_UDP_EX) != 0) - dpdkRssHF |= ETH_RSS_IPV6_UDP_EX; - - if ((rssHF & RSS_PORT) != 0) - dpdkRssHF |= ETH_RSS_PORT; - - if ((rssHF & RSS_VXLAN) != 0) - dpdkRssHF |= ETH_RSS_VXLAN; - - if ((rssHF & RSS_GENEVE) != 0) - dpdkRssHF |= ETH_RSS_GENEVE; - - if ((rssHF & RSS_NVGRE) != 0) - dpdkRssHF |= ETH_RSS_NVGRE; - - return dpdkRssHF; -} - -uint64_t DpdkDevice::convertDpdkRssHfToRssHf(uint64_t dpdkRssHF) const -{ - uint64_t rssHF = 0; - - if ((dpdkRssHF & ETH_RSS_IPV4) != 0) - rssHF |= RSS_IPV4; - - if ((dpdkRssHF & ETH_RSS_FRAG_IPV4) != 0) - rssHF |= RSS_FRAG_IPV4; - - if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV4_TCP) != 0) - rssHF |= RSS_NONFRAG_IPV4_TCP; - - if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV4_UDP) != 0) - rssHF |= RSS_NONFRAG_IPV4_UDP; - - if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV4_SCTP) != 0) - rssHF |= RSS_NONFRAG_IPV4_SCTP; - - if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV4_OTHER) != 0) - rssHF |= RSS_NONFRAG_IPV4_OTHER; - - if ((dpdkRssHF & ETH_RSS_IPV6) != 0) - rssHF |= RSS_IPV6; - - if ((dpdkRssHF & ETH_RSS_FRAG_IPV6) != 0) - rssHF |= RSS_FRAG_IPV6; - - if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV6_TCP) != 0) - rssHF |= RSS_NONFRAG_IPV6_TCP; - - if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV6_UDP) != 0) - rssHF |= RSS_NONFRAG_IPV6_UDP; - - if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV6_SCTP) != 0) - rssHF |= RSS_NONFRAG_IPV6_SCTP; - - if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV6_OTHER) != 0) - rssHF |= RSS_NONFRAG_IPV6_OTHER; - - if ((dpdkRssHF & ETH_RSS_L2_PAYLOAD) != 0) - rssHF |= RSS_L2_PAYLOAD; - - if ((dpdkRssHF & ETH_RSS_IPV6_EX) != 0) - rssHF |= RSS_IPV6_EX; - - if ((dpdkRssHF & ETH_RSS_IPV6_TCP_EX) != 0) - rssHF |= RSS_IPV6_TCP_EX; - - if ((dpdkRssHF & ETH_RSS_IPV6_UDP_EX) != 0) - rssHF |= RSS_IPV6_UDP_EX; - - if ((dpdkRssHF & ETH_RSS_PORT) != 0) - rssHF |= RSS_PORT; - - if ((dpdkRssHF & ETH_RSS_VXLAN) != 0) - rssHF |= RSS_VXLAN; - - if ((dpdkRssHF & ETH_RSS_GENEVE) != 0) - rssHF |= RSS_GENEVE; - - if ((dpdkRssHF & ETH_RSS_NVGRE) != 0) - rssHF |= RSS_NVGRE; - - return rssHF; -} - -bool DpdkDevice::isDeviceSupportRssHashFunction(DpdkRssHashFunction rssHF) const -{ - return isDeviceSupportRssHashFunction((uint64_t)rssHF); -} - -bool DpdkDevice::isDeviceSupportRssHashFunction(uint64_t rssHFMask) const -{ - uint64_t dpdkRssHF = convertRssHfToDpdkRssHf(rssHFMask); - - rte_eth_dev_info devInfo; - rte_eth_dev_info_get(m_Id, &devInfo); - - return ((devInfo.flow_type_rss_offloads & dpdkRssHF) == dpdkRssHF); -} - -uint64_t DpdkDevice::getSupportedRssHashFunctions() const -{ - rte_eth_dev_info devInfo; - rte_eth_dev_info_get(m_Id, &devInfo); - - return convertDpdkRssHfToRssHf(devInfo.flow_type_rss_offloads); -} - -uint64_t DpdkDevice::getConfiguredRssHashFunction() const -{ - if (m_Config.rssHashFunction == static_cast(RSS_DEFAULT)) - { - if (m_PMDType == PMD_I40E || m_PMDType == PMD_I40EVF) - { - return RSS_NONFRAG_IPV4_TCP | RSS_NONFRAG_IPV4_UDP | RSS_NONFRAG_IPV4_OTHER | RSS_FRAG_IPV4 | RSS_NONFRAG_IPV6_TCP | RSS_NONFRAG_IPV6_UDP | RSS_NONFRAG_IPV6_OTHER | RSS_FRAG_IPV6; - } - else - { - return RSS_IPV4 | RSS_IPV6; - } - - } - - if (m_Config.rssHashFunction == static_cast(RSS_ALL_SUPPORTED)) - { - return getSupportedRssHashFunctions(); - } - - return m_Config.rssHashFunction; -} - -std::vector DpdkDevice::rssHashFunctionMaskToString(uint64_t rssHFMask) const -{ - std::vector result = std::vector(); - - if (rssHFMask == RSS_NONE) - { - result.push_back("RSS_NONE"); - return result; - } - - if ((rssHFMask & RSS_IPV4) != 0) - result.push_back("RSS_IPV4"); - - if ((rssHFMask & RSS_FRAG_IPV4) != 0) - result.push_back("RSS_FRAG_IPV4"); - - if ((rssHFMask & RSS_NONFRAG_IPV4_TCP) != 0) - result.push_back("RSS_NONFRAG_IPV4_TCP"); - - if ((rssHFMask & RSS_NONFRAG_IPV4_UDP) != 0) - result.push_back("RSS_NONFRAG_IPV4_UDP"); - - if ((rssHFMask & RSS_NONFRAG_IPV4_SCTP) != 0) - result.push_back("RSS_NONFRAG_IPV4_SCTP"); - - if ((rssHFMask & RSS_NONFRAG_IPV4_OTHER) != 0) - result.push_back("RSS_NONFRAG_IPV4_OTHER"); - - if ((rssHFMask & RSS_IPV6) != 0) - result.push_back("RSS_IPV6"); - - if ((rssHFMask & RSS_FRAG_IPV6) != 0) - result.push_back("RSS_FRAG_IPV6"); - - if ((rssHFMask & RSS_NONFRAG_IPV6_TCP) != 0) - result.push_back("RSS_NONFRAG_IPV6_TCP"); - - if ((rssHFMask & RSS_NONFRAG_IPV6_UDP) != 0) - result.push_back("RSS_NONFRAG_IPV6_UDP"); - - if ((rssHFMask & RSS_NONFRAG_IPV6_SCTP) != 0) - result.push_back("RSS_NONFRAG_IPV6_SCTP"); - - if ((rssHFMask & RSS_NONFRAG_IPV6_OTHER) != 0) - result.push_back("RSS_NONFRAG_IPV6_OTHER"); - - if ((rssHFMask & RSS_L2_PAYLOAD) != 0) - result.push_back("RSS_L2_PAYLOAD"); - - if ((rssHFMask & RSS_IPV6_EX) != 0) - result.push_back("RSS_IPV6_EX"); - - if ((rssHFMask & RSS_IPV6_TCP_EX) != 0) - result.push_back("RSS_IPV6_TCP_EX"); - - if ((rssHFMask & RSS_IPV6_UDP_EX) != 0) - result.push_back("RSS_IPV6_UDP_EX"); - - if ((rssHFMask & RSS_PORT) != 0) - result.push_back("RSS_PORT"); - - if ((rssHFMask & RSS_VXLAN) != 0) - result.push_back("RSS_VXLAN"); - - if ((rssHFMask & RSS_GENEVE) != 0) - result.push_back("RSS_GENEVE"); - - if ((rssHFMask & RSS_NVGRE) != 0) - result.push_back("RSS_NVGRE"); - - return result; -} - - -} // namespace pcpp - -#endif /* USE_DPDK */ +#ifdef USE_DPDK + +#define LOG_MODULE PcapLogModuleDpdkDevice + +#define __STDC_LIMIT_MACROS +#define __STDC_FORMAT_MACROS + +#include "DpdkDevice.h" +#include "DpdkDeviceList.h" +#include "Logger.h" +#include "rte_version.h" +#if (RTE_VER_YEAR > 17) || (RTE_VER_YEAR == 17 && RTE_VER_MONTH >= 11) +#include "rte_bus_pci.h" +#endif +#include "rte_pci.h" +#include "rte_config.h" +#include "rte_ethdev.h" +#include "rte_errno.h" +#include "rte_malloc.h" +#include "rte_cycles.h" +#include +#include +#include + +#define MAX_BURST_SIZE 64 + +#define MEMPOOL_CACHE_SIZE 256 + +#if (RTE_VER_YEAR < 21) || (RTE_VER_YEAR == 21 && RTE_VER_MONTH < 11) +#define GET_MASTER_CORE rte_get_master_lcore +#else +#define GET_MASTER_CORE rte_get_main_lcore +#endif + +namespace pcpp +{ + +/** + * ================ + * Class DpdkDevice + * ================ + */ + +#define DPDK_COFIG_HEADER_SPLIT 0 /**< Header Split disabled */ +#define DPDK_COFIG_SPLIT_HEADER_SIZE 0 +#define DPDK_COFIG_HW_IP_CHECKSUM 0 /**< IP checksum offload disabled */ +#define DPDK_COFIG_HW_VLAN_FILTER 0 /**< VLAN filtering disabled */ +#define DPDK_COFIG_JUMBO_FRAME 0 /**< Jumbo Frame Support disabled */ +#define DPDK_COFIG_HW_STRIP_CRC 0 /**< CRC stripped by hardware disabled */ +#if (RTE_VER_YEAR < 21) || (RTE_VER_YEAR == 21 && RTE_VER_MONTH < 11) +#define DPDK_CONFIG_MQ_MODE ETH_RSS +#else +#define DPDK_CONFIG_MQ_MODE RTE_ETH_MQ_RX_RSS +#endif + + +//RSS random key: +uint8_t DpdkDevice::m_RSSKey[40] = { + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, + }; + +DpdkDevice::DpdkDevice(int port, uint32_t mBufPoolSize) + : m_Id(port), m_MacAddress(MacAddress::Zero) +{ + std::ostringstream deviceNameStream; + deviceNameStream << "DPDK_" << m_Id; + m_DeviceName = deviceNameStream.str(); + +#if (RTE_VER_YEAR > 19) || (RTE_VER_YEAR == 19 && RTE_VER_MONTH >= 8) + struct rte_ether_addr etherAddr; +#else + struct ether_addr etherAddr; +#endif + rte_eth_macaddr_get((uint8_t) m_Id, ðerAddr); + m_MacAddress = MacAddress(etherAddr.addr_bytes[0], etherAddr.addr_bytes[1], + etherAddr.addr_bytes[2], etherAddr.addr_bytes[3], + etherAddr.addr_bytes[4], etherAddr.addr_bytes[5]); + + rte_eth_dev_get_mtu((uint8_t) m_Id, &m_DeviceMtu); + + char mBufMemPoolName[32]; + sprintf(mBufMemPoolName, "MBufMemPool%d", m_Id); + if (!initMemPool(m_MBufMempool, mBufMemPoolName, mBufPoolSize)) + { + PCPP_LOG_ERROR("Could not initialize mBuf mempool. Device not initialized"); + return; + } + + m_NumOfRxQueuesOpened = 0; + m_NumOfTxQueuesOpened = 0; + + setDeviceInfo(); + + memset(&m_PrevStats, 0 ,sizeof(m_PrevStats)); + + m_TxBuffers = NULL; + m_TxBufferLastDrainTsc = NULL; + + m_DeviceOpened = false; + m_WasOpened = false; + m_StopThread = true; +} + +DpdkDevice::~DpdkDevice() +{ + if (m_TxBuffers != NULL) + delete [] m_TxBuffers; + + if (m_TxBufferLastDrainTsc != NULL) + delete [] m_TxBufferLastDrainTsc; +} + +uint32_t DpdkDevice::getCurrentCoreId() const +{ + return rte_lcore_id(); +} + +bool DpdkDevice::setMtu(uint16_t newMtu) +{ + int res = rte_eth_dev_set_mtu(m_Id, newMtu); + if (res != 0) + { + PCPP_LOG_ERROR("Couldn't set device MTU. DPDK error: " << res); + return false; + } + + PCPP_LOG_DEBUG("Managed to set MTU from " << m_DeviceMtu << " to " << newMtu); + m_DeviceMtu = newMtu; + return true; +} + +bool DpdkDevice::openMultiQueues(uint16_t numOfRxQueuesToOpen, uint16_t numOfTxQueuesToOpen, const DpdkDeviceConfiguration& config) +{ + if (m_DeviceOpened) + { + PCPP_LOG_ERROR("Device already opened"); + return false; + } + + // There is a VMXNET3 limitation that when opening a device with a certain number of RX+TX queues + // it's impossible to close it and open it again with a larger number of RX+TX queues. So for this + // PMD I made a patch to open the device in the first time with maximum RX & TX queue, close it + // immediately and open it again with number of queues the user wanted to + if (!m_WasOpened && m_PMDType == PMD_VMXNET3) + { + m_WasOpened = true; + openMultiQueues(getTotalNumOfRxQueues(), getTotalNumOfTxQueues(), config); + close(); + } + + m_Config = config; + + if (!configurePort(numOfRxQueuesToOpen, numOfTxQueuesToOpen)) + { + m_DeviceOpened = false; + return false; + } + + clearCoreConfiguration(); + + if (!initQueues(numOfRxQueuesToOpen, numOfTxQueuesToOpen)) + return false; + + if (!startDevice()) + { + PCPP_LOG_ERROR("failed to start device " << m_Id); + m_DeviceOpened = false; + return false; + } + + m_NumOfRxQueuesOpened = numOfRxQueuesToOpen; + m_NumOfTxQueuesOpened = numOfTxQueuesToOpen; + + rte_eth_stats_reset(m_Id); + + m_DeviceOpened = true; + return m_DeviceOpened; +} + + +void DpdkDevice::close() +{ + if (!m_DeviceOpened) + { + PCPP_LOG_DEBUG("Trying to close device [" << m_DeviceName << "] but device is already closed"); + return; + } + stopCapture(); + clearCoreConfiguration(); + m_NumOfRxQueuesOpened = 0; + m_NumOfTxQueuesOpened = 0; + rte_eth_dev_stop(m_Id); + PCPP_LOG_DEBUG("Called rte_eth_dev_stop for device [" << m_DeviceName << "]"); + + if (m_TxBuffers != NULL) + { + delete [] m_TxBuffers; + m_TxBuffers = NULL; + } + + if (m_TxBufferLastDrainTsc != NULL) + { + delete [] m_TxBufferLastDrainTsc; + m_TxBufferLastDrainTsc = NULL; + } + + m_DeviceOpened = false; +} + + +bool DpdkDevice::configurePort(uint8_t numOfRxQueues, uint8_t numOfTxQueues) +{ + if (numOfRxQueues > getTotalNumOfRxQueues()) + { + PCPP_LOG_ERROR("Could not open more than " << getTotalNumOfRxQueues() << " RX queues"); + return false; + } + + if (numOfTxQueues > getTotalNumOfTxQueues()) + { + PCPP_LOG_ERROR("Could not open more than " << getTotalNumOfTxQueues() << " TX queues"); + return false; + } + + // if PMD doesn't support RSS, set RSS HF to 0 + if (getSupportedRssHashFunctions() == 0 && getConfiguredRssHashFunction() != 0) + { + PCPP_LOG_DEBUG("PMD '" << m_PMDName << "' doesn't support RSS, setting RSS hash functions to 0"); + m_Config.rssHashFunction = RSS_NONE; + } + + if (!isDeviceSupportRssHashFunction(getConfiguredRssHashFunction())) + { + PCPP_LOG_ERROR("PMD '" << m_PMDName << "' doesn't support the request RSS hash functions 0x" << std::hex << getConfiguredRssHashFunction()); + return false; + } + + // verify num of RX queues is power of 2 + bool isRxQueuePowerOfTwo = !(numOfRxQueues == 0) && !(numOfRxQueues & (numOfRxQueues - 1)); + if (!isRxQueuePowerOfTwo) + { + PCPP_LOG_ERROR("Num of RX queues must be power of 2 (because of DPDK limitation). Attempted to open device with " << numOfRxQueues << " RX queues"); + return false; + } + + struct rte_eth_conf portConf; + memset(&portConf,0,sizeof(rte_eth_conf)); + portConf.rxmode.split_hdr_size = DPDK_COFIG_SPLIT_HEADER_SIZE; +#if (RTE_VER_YEAR < 18) || (RTE_VER_YEAR == 18 && RTE_VER_MONTH < 8) + portConf.rxmode.header_split = DPDK_COFIG_HEADER_SPLIT; + portConf.rxmode.hw_ip_checksum = DPDK_COFIG_HW_IP_CHECKSUM; + portConf.rxmode.hw_vlan_filter = DPDK_COFIG_HW_VLAN_FILTER; + portConf.rxmode.jumbo_frame = DPDK_COFIG_JUMBO_FRAME; + portConf.rxmode.hw_strip_crc = DPDK_COFIG_HW_STRIP_CRC; +#endif + portConf.rxmode.mq_mode = DPDK_CONFIG_MQ_MODE; + portConf.rx_adv_conf.rss_conf.rss_key = m_Config.rssKey; + portConf.rx_adv_conf.rss_conf.rss_key_len = m_Config.rssKeyLength; + portConf.rx_adv_conf.rss_conf.rss_hf = convertRssHfToDpdkRssHf(getConfiguredRssHashFunction()); + + int res = rte_eth_dev_configure((uint8_t) m_Id, numOfRxQueues, numOfTxQueues, &portConf); + if (res < 0) + { + PCPP_LOG_ERROR("Failed to configure device [" << m_DeviceName << "]. error is: '" << rte_strerror(res) << "' [Error code: " << res << "]"); + return false; + } + + PCPP_LOG_DEBUG("Successfully called rte_eth_dev_configure for device [" << m_DeviceName << "] with " << numOfRxQueues << " RX queues and " << numOfTxQueues << " TX queues"); + + return true; +} + +bool DpdkDevice::initQueues(uint8_t numOfRxQueuesToInit, uint8_t numOfTxQueuesToInit) +{ + rte_eth_dev_info devInfo; + rte_eth_dev_info_get(m_Id, &devInfo); + if (numOfRxQueuesToInit > devInfo.max_rx_queues) + { + PCPP_LOG_ERROR("Num of RX queues requested for open [" << numOfRxQueuesToInit << "] is larger than RX queues available in NIC [" << devInfo.max_rx_queues << "]"); + return false; + } + + if (numOfTxQueuesToInit > devInfo.max_tx_queues) + { + PCPP_LOG_ERROR("Num of TX queues requested for open [" << numOfTxQueuesToInit << "] is larger than TX queues available in NIC [" << devInfo.max_tx_queues << "]"); + return false; + } + + for (uint8_t i = 0; i < numOfRxQueuesToInit; i++) + { + int ret = rte_eth_rx_queue_setup((uint8_t) m_Id, i, + m_Config.receiveDescriptorsNumber, 0, + NULL, m_MBufMempool); + + if (ret < 0) + { + PCPP_LOG_ERROR("Failed to init RX queue for device [" << m_DeviceName << "]. Error was: '" << rte_strerror(ret) << "' [Error code: " << ret << "]"); + return false; + } + } + + PCPP_LOG_DEBUG("Successfully initialized " << numOfRxQueuesToInit << " RX queues for device [" << m_DeviceName << "]"); + + for (uint8_t i = 0; i < numOfTxQueuesToInit; i++) + { + int ret = rte_eth_tx_queue_setup((uint8_t) m_Id, i, + m_Config.transmitDescriptorsNumber, + 0, NULL); + if (ret < 0) + { + PCPP_LOG_ERROR("Failed to init TX queue #" << i << " for port " << m_Id << ". Error was: '" << rte_strerror(ret) << "' [Error code: " << ret << "]"); + return false; + } + } + + if (m_TxBuffers != NULL) + delete [] m_TxBuffers; + + if (m_TxBufferLastDrainTsc != NULL) + delete [] m_TxBufferLastDrainTsc; + + m_TxBuffers = new rte_eth_dev_tx_buffer*[numOfTxQueuesToInit]; + m_TxBufferLastDrainTsc = new uint64_t[numOfTxQueuesToInit]; + memset(m_TxBufferLastDrainTsc, 0, sizeof(uint64_t)*numOfTxQueuesToInit); + + for (uint8_t i = 0; i < numOfTxQueuesToInit; i++) + { + m_TxBuffers[i] = (rte_eth_dev_tx_buffer*)rte_zmalloc_socket("tx_buffer", RTE_ETH_TX_BUFFER_SIZE(MAX_BURST_SIZE), 0, rte_eth_dev_socket_id(m_Id)); + + if (m_TxBuffers[i] == NULL) + { + PCPP_LOG_ERROR("Failed to allocate TX buffer for port " << m_Id << " TX queue " << (int)i); + return false; + } + + int res = rte_eth_tx_buffer_init(m_TxBuffers[i], MAX_BURST_SIZE); + + if (res != 0) + { + PCPP_LOG_ERROR("Failed to init TX buffer for port " << m_Id << " TX queue " << (int)i); + return false; + } + } + + m_TxBufferDrainTsc = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S * m_Config.flushTxBufferTimeout; + + memset(m_TxBufferLastDrainTsc, 0, sizeof(uint64_t)*numOfTxQueuesToInit); + + PCPP_LOG_DEBUG("Successfully initialized " << numOfTxQueuesToInit << " TX queues for device [" << m_DeviceName << "]"); + + return true; +} + + +bool DpdkDevice::initMemPool(struct rte_mempool*& memPool, const char* mempoolName, uint32_t mBufPoolSize) +{ + bool ret = false; + + // create mbuf pool + memPool = rte_pktmbuf_pool_create(mempoolName, mBufPoolSize, MEMPOOL_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); + if (memPool == NULL) + { + PCPP_LOG_ERROR("Failed to create packets memory pool for port " << m_Id << ", pool name: " << mempoolName << ". Error was: '" << rte_strerror(rte_errno) << "' [Error code: " << rte_errno << "]"); + } + else + { + PCPP_LOG_DEBUG("Successfully initialized packets pool of size [" << mBufPoolSize << "] for device [" << m_DeviceName << "]"); + ret = true; + } + return ret; +} + +bool DpdkDevice::startDevice() +{ + int ret = rte_eth_dev_start((uint8_t) m_Id); + if (ret < 0) + { + PCPP_LOG_ERROR("Failed to start device " << m_Id << ". Error is " << ret); + return false; + } + + LinkStatus status; + getLinkStatus(status); + if (Logger::getInstance().isDebugEnabled(PcapLogModuleDpdkDevice)) + { + std::string linkStatus = (status.linkUp ? "up" : "down"); + std::string linkDuplex = (status.linkDuplex == LinkStatus::FULL_DUPLEX ? "full-duplex" : "half-duplex"); + PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] : Link " << linkStatus << "; Speed: " << status.linkSpeedMbps << " Mbps; " << linkDuplex); + } + + rte_eth_promiscuous_enable((uint8_t) m_Id); + PCPP_LOG_DEBUG("Started device [" << m_DeviceName << "]"); + + return true; +} + + +void DpdkDevice::clearCoreConfiguration() +{ + for (int i = 0; i < MAX_NUM_OF_CORES; i++) + { + m_CoreConfiguration[i].IsCoreInUse = false; + } +} + +int DpdkDevice::getCoresInUseCount() const +{ + int res = 0; + for (int i = 0; i < MAX_NUM_OF_CORES; i++) + if (m_CoreConfiguration[i].IsCoreInUse) + res++; + + return res; +} + +void DpdkDevice::setDeviceInfo() +{ + rte_eth_dev_info portInfo; + rte_eth_dev_info_get(m_Id, &portInfo); + m_PMDName = std::string(portInfo.driver_name); + + if (m_PMDName == "eth_bond") + m_PMDType = PMD_BOND; + else if (m_PMDName == "rte_em_pmd") + m_PMDType = PMD_E1000EM; + else if (m_PMDName == "rte_igb_pmd") + m_PMDType = PMD_IGB; + else if (m_PMDName == "rte_igbvf_pmd") + m_PMDType = PMD_IGBVF; + else if (m_PMDName == "rte_enic_pmd") + m_PMDType = PMD_ENIC; + else if (m_PMDName == "rte_pmd_fm10k") + m_PMDType = PMD_FM10K; + else if (m_PMDName == "rte_i40e_pmd" || m_PMDName == "net_i40e") + m_PMDType = PMD_I40E; + else if (m_PMDName == "rte_i40evf_pmd") + m_PMDType = PMD_I40EVF; + else if (m_PMDName == "rte_ixgbe_pmd") + m_PMDType = PMD_IXGBE; + else if (m_PMDName == "rte_ixgbevf_pmd") + m_PMDType = PMD_IXGBEVF; + else if (m_PMDName == "librte_pmd_mlx4") + m_PMDType = PMD_MLX4; + else if (m_PMDName == "eth_null") + m_PMDType = PMD_NULL; + else if (m_PMDName == "eth_pcap") + m_PMDType = PMD_PCAP; + else if (m_PMDName == "eth_ring") + m_PMDType = PMD_RING; + else if (m_PMDName == "rte_virtio_pmd") + m_PMDType = PMD_VIRTIO; + else if (m_PMDName == "rte_vmxnet3_pmd") + m_PMDType = PMD_VMXNET3; + else if (m_PMDName == "eth_xenvirt") + m_PMDType = PMD_XENVIRT; + else + m_PMDType = PMD_UNKNOWN; + +#if (RTE_VER_YEAR < 18) || (RTE_VER_YEAR == 18 && RTE_VER_MONTH < 5) // before 18.05 + char pciName[30]; + #if (RTE_VER_YEAR > 17) || (RTE_VER_YEAR == 17 && RTE_VER_MONTH >= 11) // 17.11 - 18.02 + rte_pci_device_name(&(portInfo.pci_dev->addr), pciName, 30); + #else // 16.11 - 17.11 + rte_eal_pci_device_name(&(portInfo.pci_dev->addr), pciName, 30); + #endif + m_PciAddress = std::string(pciName); +#else // 18.05 forward + m_PciAddress = std::string(portInfo.device->name); +#endif + + PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] has " << portInfo.max_rx_queues << " RX queues"); + PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] has " << portInfo.max_tx_queues << " TX queues"); + + m_TotalAvailableRxQueues = portInfo.max_rx_queues; + m_TotalAvailableTxQueues = portInfo.max_tx_queues; +} + + +bool DpdkDevice::isVirtual() const +{ + switch (m_PMDType) + { + case PMD_IGBVF: + case PMD_I40EVF: + case PMD_IXGBEVF: + case PMD_PCAP: + case PMD_RING: + case PMD_VIRTIO: + case PMD_VMXNET3: + case PMD_XENVIRT: + return true; + default: + return false; + } +} + + +void DpdkDevice::getLinkStatus(LinkStatus& linkStatus) const +{ + struct rte_eth_link link; + rte_eth_link_get((uint8_t) m_Id, &link); + linkStatus.linkUp = link.link_status; + linkStatus.linkSpeedMbps = (unsigned) link.link_speed; + linkStatus.linkDuplex = (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? LinkStatus::FULL_DUPLEX : LinkStatus::HALF_DUPLEX; +} + + +bool DpdkDevice::initCoreConfigurationByCoreMask(CoreMask coreMask) +{ + int i = 0; + int numOfCores = getNumOfCores(); + clearCoreConfiguration(); + while ((coreMask != 0) && (i < numOfCores)) + { + if (coreMask & 1) + { + if (i == DpdkDeviceList::getInstance().getDpdkMasterCore().Id) + { + PCPP_LOG_ERROR("Core " << i << " is the master core, you can't use it for capturing threads"); + clearCoreConfiguration(); + return false; + } + + if (!rte_lcore_is_enabled(i)) + { + PCPP_LOG_ERROR("Trying to use core #" << i << " which isn't initialized by DPDK"); + clearCoreConfiguration(); + return false; + } + m_CoreConfiguration[i].IsCoreInUse = true; + } + + coreMask = coreMask >> 1; + i++; + } + + if (coreMask != 0) // this mean coreMask contains a core that doesn't exist + { + PCPP_LOG_ERROR("Trying to use a core [" << i << "] that doesn't exist while machine has " << numOfCores << " cores"); + clearCoreConfiguration(); + return false; + } + + return true; +} + + +bool DpdkDevice::startCaptureSingleThread(OnDpdkPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie) +{ + if (!m_StopThread) + { + PCPP_LOG_ERROR("Device already capturing. Cannot start 2 capture sessions at the same time"); + return false; + } + + if (m_NumOfRxQueuesOpened != 1) + { + PCPP_LOG_ERROR("Cannot start capturing on a single thread when more than 1 RX queue is opened"); + return false; + } + + PCPP_LOG_DEBUG("Trying to start capturing on a single thread for device [" << m_DeviceName << "]"); + + clearCoreConfiguration(); + + m_OnPacketsArriveCallback = onPacketsArrive; + m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; + + m_StopThread = false; + + for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) + { + if (coreId == (int)GET_MASTER_CORE() || !rte_lcore_is_enabled(coreId)) + continue; + + m_CoreConfiguration[coreId].IsCoreInUse = true; + m_CoreConfiguration[coreId].RxQueueId = 0; + + PCPP_LOG_DEBUG("Trying to start capturing on core " << coreId); + int err = rte_eal_remote_launch(dpdkCaptureThreadMain, (void*)this, coreId); + if (err != 0) + { + PCPP_LOG_ERROR("Cannot create capture thread for device '" << m_DeviceName << "'"); + m_CoreConfiguration[coreId].IsCoreInUse = false; + return false; + } + + PCPP_LOG_DEBUG("Capturing started for device [" << m_DeviceName << "]"); + return true; + } + + PCPP_LOG_ERROR("Could not find initialized core so capturing thread cannot be initialized"); + return false; +} + +bool DpdkDevice::startCaptureMultiThreads(OnDpdkPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie, CoreMask coreMask) +{ + if (!m_DeviceOpened) + { + PCPP_LOG_ERROR("Device not opened"); + return false; + } + + if (!initCoreConfigurationByCoreMask(coreMask)) + return false; + + if (m_NumOfRxQueuesOpened != getCoresInUseCount()) + { + PCPP_LOG_ERROR("Cannot use a different number of queues and cores. Opened " << m_NumOfRxQueuesOpened << " queues but set " << getCoresInUseCount() << " cores in core mask"); + clearCoreConfiguration(); + return false; + } + + m_StopThread = false; + int rxQueue = 0; + for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) + { + if (!m_CoreConfiguration[coreId].IsCoreInUse) + continue; + + // create a new thread + m_CoreConfiguration[coreId].RxQueueId = rxQueue++; + int err = rte_eal_remote_launch(dpdkCaptureThreadMain, (void*)this, coreId); + if (err != 0) + { + PCPP_LOG_ERROR("Cannot create capture thread #" << coreId << " for device '" << m_DeviceName << "': [" << strerror(err) << "]"); + m_CoreConfiguration[coreId].clear(); + return false; + } + } + + m_OnPacketsArriveCallback = onPacketsArrive; + m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; + + return true; +} + +void DpdkDevice::stopCapture() +{ + PCPP_LOG_DEBUG("Trying to stop capturing on device [" << m_DeviceName << "]"); + m_StopThread = true; + for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) + { + if (!m_CoreConfiguration[coreId].IsCoreInUse) + continue; + rte_eal_wait_lcore(coreId); + PCPP_LOG_DEBUG("Thread on core [" << coreId << "] stopped"); + } + + PCPP_LOG_DEBUG("All capturing threads stopped"); +} + +int DpdkDevice::dpdkCaptureThreadMain(void *ptr) +{ + DpdkDevice* pThis = (DpdkDevice*)ptr; + struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; + + if (pThis == NULL) + { + PCPP_LOG_ERROR("Failed to retrieve DPDK device in capture thread main loop"); + return 1; + } + + uint32_t coreId = pThis->getCurrentCoreId(); + PCPP_LOG_DEBUG("Starting capture thread " << coreId); + + int queueId = pThis->m_CoreConfiguration[coreId].RxQueueId; + + while (likely(!pThis->m_StopThread)) + { + uint32_t numOfPktsReceived = rte_eth_rx_burst(pThis->m_Id, queueId, mBufArray, MAX_BURST_SIZE); + + if (unlikely(numOfPktsReceived == 0)) + continue; + + timespec time; + clock_gettime(CLOCK_REALTIME, &time); + + if (likely(pThis->m_OnPacketsArriveCallback != NULL)) + { + MBufRawPacket rawPackets[MAX_BURST_SIZE]; + for (uint32_t index = 0; index < numOfPktsReceived; ++index) + { + rawPackets[index].setMBuf(mBufArray[index], time); + } + + pThis->m_OnPacketsArriveCallback(rawPackets, numOfPktsReceived, coreId, pThis, pThis->m_OnPacketsArriveUserCookie); + } + } + + PCPP_LOG_DEBUG("Exiting capture thread " << coreId); + + return 0; +} + +#define nanosec_gap(begin, end) ((end.tv_sec - begin.tv_sec) * 1000000000.0 + (end.tv_nsec - begin.tv_nsec)) + +void DpdkDevice::getStatistics(DpdkDeviceStats& stats) const +{ + timespec timestamp; + clock_gettime(CLOCK_MONOTONIC, ×tamp); + struct rte_eth_stats rteStats; + rte_eth_stats_get(m_Id, &rteStats); + + double secsElapsed = (double)nanosec_gap(m_PrevStats.timestamp, timestamp) / 1000000000.0; + + stats.devId = m_Id; + stats.timestamp = timestamp; + stats.rxErroneousPackets = rteStats.ierrors; + stats.rxMbufAlocFailed = rteStats.rx_nombuf; + stats.rxPacketsDroppedByHW = rteStats.imissed; + stats.aggregatedRxStats.packets = rteStats.ipackets; + stats.aggregatedRxStats.bytes = rteStats.ibytes; + stats.aggregatedRxStats.packetsPerSec = (stats.aggregatedRxStats.packets - m_PrevStats.aggregatedRxStats.packets) / secsElapsed; + stats.aggregatedRxStats.bytesPerSec = (stats.aggregatedRxStats.bytes - m_PrevStats.aggregatedRxStats.bytes) / secsElapsed; + stats.aggregatedTxStats.packets = rteStats.opackets; + stats.aggregatedTxStats.bytes = rteStats.obytes; + stats.aggregatedTxStats.packetsPerSec = (stats.aggregatedTxStats.packets - m_PrevStats.aggregatedTxStats.packets) / secsElapsed; + stats.aggregatedTxStats.bytesPerSec = (stats.aggregatedTxStats.bytes - m_PrevStats.aggregatedTxStats.bytes) / secsElapsed; + + int numRxQs = std::min(DPDK_MAX_RX_QUEUES, RTE_ETHDEV_QUEUE_STAT_CNTRS); + int numTxQs = std::min(DPDK_MAX_TX_QUEUES, RTE_ETHDEV_QUEUE_STAT_CNTRS); + + for (int i = 0; i < numRxQs; i++) + { + stats.rxStats[i].packets = rteStats.q_ipackets[i]; + stats.rxStats[i].bytes = rteStats.q_ibytes[i]; + stats.rxStats[i].packetsPerSec = (stats.rxStats[i].packets - m_PrevStats.rxStats[i].packets) / secsElapsed; + stats.rxStats[i].bytesPerSec = (stats.rxStats[i].bytes - m_PrevStats.rxStats[i].bytes) / secsElapsed; + } + + for (int i = 0; i < numTxQs; i++) + { + stats.txStats[i].packets = rteStats.q_opackets[i]; + stats.txStats[i].bytes = rteStats.q_obytes[i]; + stats.txStats[i].packetsPerSec = (stats.txStats[i].packets - m_PrevStats.txStats[i].packets) / secsElapsed; + stats.txStats[i].bytesPerSec = (stats.txStats[i].bytes - m_PrevStats.txStats[i].bytes) / secsElapsed; + } + + //m_PrevStats = stats; + memcpy(&m_PrevStats, &stats, sizeof(m_PrevStats)); +} + +void DpdkDevice::clearStatistics() +{ + rte_eth_stats_reset(m_Id); + memset(&m_PrevStats, 0 ,sizeof(m_PrevStats)); +} + + +bool DpdkDevice::setFilter(GeneralFilter& filter) +{ + //TODO: I think DPDK supports filters + PCPP_LOG_ERROR("Filters aren't supported in DPDK device"); + return false; +} + +bool DpdkDevice::setFilter(std::string filterAsString) +{ + //TODO: I think DPDK supports filters + PCPP_LOG_ERROR("Filters aren't supported in DPDK device"); + return false; +} + +uint16_t DpdkDevice::receivePackets(MBufRawPacketVector& rawPacketsArr, uint16_t rxQueueId) const +{ + if (!m_DeviceOpened) + { + PCPP_LOG_ERROR("Device not opened"); + return 0; + } + + if (!m_StopThread) + { + PCPP_LOG_ERROR("DpdkDevice capture mode is currently running. Cannot receive packets in parallel"); + return 0; + } + + if (rxQueueId >= m_TotalAvailableRxQueues) + { + PCPP_LOG_ERROR("RX queue ID #" << rxQueueId << " not available for this device"); + return 0; + } + + struct rte_mbuf* mBufArray[MAX_BURST_SIZE]; + uint32_t numOfPktsReceived = rte_eth_rx_burst(m_Id, rxQueueId, mBufArray, MAX_BURST_SIZE); + + //the following line trashes the log with many messages. Uncomment only if necessary + //PCPP_LOG_DEBUG("Captured %d packets", numOfPktsReceived); + + if (unlikely(!numOfPktsReceived)) + { + return 0; + } + + timespec time; + clock_gettime(CLOCK_REALTIME, &time); + + for (uint32_t index = 0; index < numOfPktsReceived; ++index) + { + struct rte_mbuf* mBuf = mBufArray[index]; + MBufRawPacket* newRawPacket = new MBufRawPacket(); + newRawPacket->setMBuf(mBuf, time); + rawPacketsArr.pushBack(newRawPacket); + } + + return numOfPktsReceived; +} + +uint16_t DpdkDevice::receivePackets(MBufRawPacket** rawPacketsArr, uint16_t rawPacketArrLength, uint16_t rxQueueId) const +{ + if (unlikely(!m_DeviceOpened)) + { + PCPP_LOG_ERROR("Device not opened"); + return 0; + } + + if (unlikely(!m_StopThread)) + { + PCPP_LOG_ERROR("DpdkDevice capture mode is currently running. Cannot receive packets in parallel"); + return 0; + } + + if (unlikely(rxQueueId >= m_TotalAvailableRxQueues)) + { + PCPP_LOG_ERROR("RX queue ID #" << rxQueueId << " not available for this device"); + return 0; + } + + if (unlikely(rawPacketsArr == NULL)) + { + PCPP_LOG_ERROR("Provided address of array to store packets is NULL"); + return 0; + } + + struct rte_mbuf* mBufArray[rawPacketArrLength]; + uint16_t packetsReceived = rte_eth_rx_burst(m_Id, rxQueueId, mBufArray, rawPacketArrLength); + + if (unlikely(!packetsReceived)) + { + return 0; + } + + timespec time; + clock_gettime(CLOCK_REALTIME, &time); + + for (size_t index = 0; index < packetsReceived; ++index) + { + struct rte_mbuf* mBuf = mBufArray[index]; + if (rawPacketsArr[index] == NULL) + rawPacketsArr[index] = new MBufRawPacket(); + + rawPacketsArr[index]->setMBuf(mBuf, time); + } + + return packetsReceived; +} + +uint16_t DpdkDevice::receivePackets(Packet** packetsArr, uint16_t packetsArrLength, uint16_t rxQueueId) const +{ + if (unlikely(!m_DeviceOpened)) + { + PCPP_LOG_ERROR("Device not opened"); + return 0; + } + + if (unlikely(!m_StopThread)) + { + PCPP_LOG_ERROR("DpdkDevice capture mode is currently running. Cannot receive packets in parallel"); + return 0; + } + + if (unlikely(rxQueueId >= m_TotalAvailableRxQueues)) + { + PCPP_LOG_ERROR("RX queue ID #" << rxQueueId << " not available for this device"); + return 0; + } + + struct rte_mbuf* mBufArray[packetsArrLength]; + uint16_t packetsReceived = rte_eth_rx_burst(m_Id, rxQueueId, mBufArray, packetsArrLength); + + if (unlikely(!packetsReceived)) + { + return 0; + } + + timespec time; + clock_gettime(CLOCK_REALTIME, &time); + + for (size_t index = 0; index < packetsReceived; ++index) + { + struct rte_mbuf* mBuf = mBufArray[index]; + MBufRawPacket* newRawPacket = new MBufRawPacket(); + newRawPacket->setMBuf(mBuf, time); + if (packetsArr[index] == NULL) + packetsArr[index] = new Packet(); + + packetsArr[index]->setRawPacket(newRawPacket, true); + } + + return packetsReceived; +} + +uint16_t DpdkDevice::flushTxBuffer(bool flushOnlyIfTimeoutExpired, uint16_t txQueueId) +{ + bool flush = true; + + if (flushOnlyIfTimeoutExpired) + { + uint64_t curTsc = rte_rdtsc(); + + if (curTsc - m_TxBufferLastDrainTsc[txQueueId] > m_TxBufferDrainTsc) + m_TxBufferLastDrainTsc[txQueueId] = curTsc; + else + flush = false; + } + + if (flush) + return rte_eth_tx_buffer_flush(m_Id, txQueueId, m_TxBuffers[txQueueId]); + + return 0; +} + +static rte_mbuf* getNextPacketFromMBufRawPacketArray(void* packetStorage, int index) +{ + MBufRawPacket** packetsArr = (MBufRawPacket**)packetStorage; + return packetsArr[index]->getMBuf(); +} + +static rte_mbuf* getNextPacketFromMBufArray(void* packetStorage, int index) +{ + rte_mbuf** mbufArr = (rte_mbuf**)packetStorage; + return mbufArr[index]; +} + +static rte_mbuf* getNextPacketFromMBufRawPacketVec(void* packetStorage, int index) +{ + MBufRawPacketVector* packetVec = (MBufRawPacketVector*)packetStorage; + return packetVec->at(index)->getMBuf(); +} + +static rte_mbuf* getNextPacketFromMBufRawPacket(void* packetStorage, int index) +{ + MBufRawPacket* mbufRawPacket = (MBufRawPacket*)packetStorage; + return mbufRawPacket->getMBuf(); +} + +uint16_t DpdkDevice::sendPacketsInner(uint16_t txQueueId, void* packetStorage, PacketIterator iter, int arrLength, bool useTxBuffer) +{ + if (unlikely(!m_DeviceOpened)) + { + PCPP_LOG_ERROR("Device '" << m_DeviceName << "' not opened!"); + return 0; + } + + if (unlikely(txQueueId >= m_NumOfTxQueuesOpened)) + { + PCPP_LOG_ERROR("TX queue " << txQueueId << " isn't opened in device"); + return 0; + } + + rte_mbuf* mBufArr[MAX_BURST_SIZE]; + + int packetIndex = 0; + int mBufArrIndex = 0; + uint16_t packetsSent = 0; + int lastSleep = 0; + + #define PACKET_TRANSMISSION_THRESHOLD 0.8 + int packetTxThreshold = m_Config.transmitDescriptorsNumber*PACKET_TRANSMISSION_THRESHOLD; + + while (packetIndex < arrLength) + { + rte_mbuf* mBuf = iter(packetStorage, packetIndex); + + if (useTxBuffer) + { + packetsSent += rte_eth_tx_buffer(m_Id, txQueueId, m_TxBuffers[txQueueId], mBuf); + } + else + { + mBufArr[mBufArrIndex++] = mBuf; + + if (unlikely(mBufArrIndex == MAX_BURST_SIZE)) + { + packetsSent += rte_eth_tx_burst(m_Id, txQueueId, mBufArr, MAX_BURST_SIZE); + mBufArrIndex = 0; + + if (unlikely((packetsSent - lastSleep) >= packetTxThreshold)) + { + PCPP_LOG_DEBUG("Since NIC couldn't send all packet in this iteration, waiting for 0.2 second for H/W descriptors to get free"); + usleep(200000); + lastSleep = packetsSent; + } + } + } + + packetIndex++; + } + + if (useTxBuffer) + { + packetsSent += flushTxBuffer(true, txQueueId); + } + else if (mBufArrIndex > 0) + { + packetsSent += rte_eth_tx_burst(m_Id, txQueueId, mBufArr, mBufArrIndex); + } + + return packetsSent; +} + + +uint16_t DpdkDevice::sendPackets(MBufRawPacket** rawPacketsArr, uint16_t arrLength, uint16_t txQueueId, bool useTxBuffer) +{ + uint16_t packetsSent = sendPacketsInner(txQueueId, (void*)rawPacketsArr, getNextPacketFromMBufRawPacketArray, arrLength, useTxBuffer); + + bool needToFreeMbuf = false; + int applyForMBufs = arrLength; + + if (unlikely(!useTxBuffer && (packetsSent != arrLength))) + { + applyForMBufs = packetsSent; + } + + for (int index = 0; index < applyForMBufs; index++) + rawPacketsArr[index]->setFreeMbuf(needToFreeMbuf); + + for (int index = applyForMBufs; index < arrLength; index++) + rawPacketsArr[index]->setFreeMbuf(!needToFreeMbuf); + + return packetsSent; +} + +uint16_t DpdkDevice::sendPackets(Packet** packetsArr, uint16_t arrLength, uint16_t txQueueId, bool useTxBuffer) +{ + rte_mbuf* mBufArr[arrLength]; + MBufRawPacketVector mBufVec; + MBufRawPacket* mBufRawPacketArr[arrLength]; + + for (size_t i = 0; i < arrLength; i++) + { + MBufRawPacket* rawPacket = NULL; + uint8_t rawPacketType = packetsArr[i]->getRawPacketReadOnly()->getObjectType(); + if (rawPacketType != MBUFRAWPACKET_OBJECT_TYPE) + { + rawPacket = new MBufRawPacket(); + if (unlikely(!rawPacket->initFromRawPacket(packetsArr[i]->getRawPacketReadOnly(), this))) + { + delete rawPacket; + return 0; + } + + mBufVec.pushBack(rawPacket); + } + else + { + rawPacket = (MBufRawPacket*)packetsArr[i]->getRawPacketReadOnly(); + } + + mBufArr[i] = rawPacket->getMBuf(); + mBufRawPacketArr[i] = rawPacket; + } + + uint16_t packetsSent = sendPacketsInner(txQueueId, (void*)mBufArr, getNextPacketFromMBufArray, arrLength, useTxBuffer); + + bool needToFreeMbuf = (!useTxBuffer && (packetsSent != arrLength)); + for (int index = 0; index < arrLength; index++) + mBufRawPacketArr[index]->setFreeMbuf(needToFreeMbuf); + + return packetsSent; +} + +uint16_t DpdkDevice::sendPackets(RawPacketVector& rawPacketsVec, uint16_t txQueueId, bool useTxBuffer) +{ + size_t vecSize = rawPacketsVec.size(); + rte_mbuf* mBufArr[vecSize]; + MBufRawPacket* mBufRawPacketArr[vecSize]; + MBufRawPacketVector mBufVec; + int mBufIndex = 0; + + for (RawPacketVector::ConstVectorIterator iter = rawPacketsVec.begin(); iter != rawPacketsVec.end(); iter++) + { + MBufRawPacket* rawPacket = NULL; + uint8_t rawPacketType = (*iter)->getObjectType(); + if (rawPacketType != MBUFRAWPACKET_OBJECT_TYPE) + { + rawPacket = new MBufRawPacket(); + if (unlikely(!rawPacket->initFromRawPacket(*iter, this))) + { + delete rawPacket; + return 0; + } + + mBufVec.pushBack(rawPacket); + } + else + { + rawPacket = (MBufRawPacket*)(*iter); + } + + mBufRawPacketArr[mBufIndex] = rawPacket; + mBufArr[mBufIndex++] = rawPacket->getMBuf(); + } + + uint16_t packetsSent = sendPacketsInner(txQueueId, (void*)mBufArr, getNextPacketFromMBufArray, vecSize, useTxBuffer); + + bool needToFreeMbuf = (!useTxBuffer && (packetsSent != vecSize)); + for (size_t index = 0; index < rawPacketsVec.size(); index++) + mBufRawPacketArr[index]->setFreeMbuf(needToFreeMbuf); + + return packetsSent; +} + +uint16_t DpdkDevice::sendPackets(MBufRawPacketVector& rawPacketsVec, uint16_t txQueueId, bool useTxBuffer) +{ + size_t vecSize = rawPacketsVec.size(); + uint16_t packetsSent = sendPacketsInner(txQueueId, (void*)(&rawPacketsVec), getNextPacketFromMBufRawPacketVec, vecSize, useTxBuffer); + + bool needToFreeMbuf = (!useTxBuffer && (packetsSent != vecSize)); + + for (size_t index = 0; index < vecSize; index++) + rawPacketsVec.at(index)->setFreeMbuf(needToFreeMbuf); + + return packetsSent; +} + +bool DpdkDevice::sendPacket(RawPacket& rawPacket, uint16_t txQueueId, bool useTxBuffer) +{ + uint8_t rawPacketType = rawPacket.getObjectType(); + if (rawPacketType == MBUFRAWPACKET_OBJECT_TYPE) + { + bool packetSent = (sendPacketsInner(txQueueId, (MBufRawPacket*)&rawPacket, getNextPacketFromMBufRawPacket, 1, useTxBuffer) == 1); + bool needToFreeMbuf = (!useTxBuffer && !packetSent); + ((MBufRawPacket*)&rawPacket)->setFreeMbuf(needToFreeMbuf); + return packetSent; + } + + MBufRawPacket mbufRawPacket; + if (unlikely(!mbufRawPacket.initFromRawPacket(&rawPacket, this))) + return false; + + bool packetSent = (sendPacketsInner(txQueueId, &mbufRawPacket, getNextPacketFromMBufRawPacket, 1, useTxBuffer) == 1); + bool needToFreeMbuf = (!useTxBuffer && !packetSent); + mbufRawPacket.setFreeMbuf(needToFreeMbuf); + + return packetSent; +} + +bool DpdkDevice::sendPacket(MBufRawPacket& rawPacket, uint16_t txQueueId, bool useTxBuffer) +{ + bool packetSent = (sendPacketsInner(txQueueId, &rawPacket, getNextPacketFromMBufRawPacket, 1, useTxBuffer) == 1); + bool needToFreeMbuf = (!useTxBuffer && !packetSent); + rawPacket.setFreeMbuf(needToFreeMbuf); + return packetSent; +} + +bool DpdkDevice::sendPacket(Packet& packet, uint16_t txQueueId, bool useTxBuffer) +{ + return sendPacket(*(packet.getRawPacket()), txQueueId); +} + +int DpdkDevice::getAmountOfFreeMbufs() const +{ + return (int)rte_mempool_avail_count(m_MBufMempool); +} + +int DpdkDevice::getAmountOfMbufsInUse() const +{ + return (int)rte_mempool_in_use_count(m_MBufMempool); +} + +uint64_t DpdkDevice::convertRssHfToDpdkRssHf(uint64_t rssHF) const +{ + if (rssHF == (uint64_t)-1) + { + rte_eth_dev_info devInfo; + rte_eth_dev_info_get(m_Id, &devInfo); + return devInfo.flow_type_rss_offloads; + } + + uint64_t dpdkRssHF = 0; + + if ((rssHF & RSS_IPV4) != 0) + dpdkRssHF |= ETH_RSS_IPV4; + + if ((rssHF & RSS_FRAG_IPV4) != 0) + dpdkRssHF |= ETH_RSS_FRAG_IPV4; + + if ((rssHF & RSS_NONFRAG_IPV4_TCP) != 0) + dpdkRssHF |= ETH_RSS_NONFRAG_IPV4_TCP; + + if ((rssHF & RSS_NONFRAG_IPV4_UDP) != 0) + dpdkRssHF |= ETH_RSS_NONFRAG_IPV4_UDP; + + if ((rssHF & RSS_NONFRAG_IPV4_SCTP) != 0) + dpdkRssHF |= ETH_RSS_NONFRAG_IPV4_SCTP; + + if ((rssHF & RSS_NONFRAG_IPV4_OTHER) != 0) + dpdkRssHF |= ETH_RSS_NONFRAG_IPV4_OTHER; + + if ((rssHF & RSS_IPV6) != 0) + dpdkRssHF |= ETH_RSS_IPV6; + + if ((rssHF & RSS_FRAG_IPV6) != 0) + dpdkRssHF |= ETH_RSS_FRAG_IPV6; + + if ((rssHF & RSS_NONFRAG_IPV6_TCP) != 0) + dpdkRssHF |= ETH_RSS_NONFRAG_IPV6_TCP; + + if ((rssHF & RSS_NONFRAG_IPV6_UDP) != 0) + dpdkRssHF |= ETH_RSS_NONFRAG_IPV6_UDP; + + if ((rssHF & RSS_NONFRAG_IPV6_SCTP) != 0) + dpdkRssHF |= ETH_RSS_NONFRAG_IPV6_SCTP; + + if ((rssHF & RSS_NONFRAG_IPV6_OTHER) != 0) + dpdkRssHF |= ETH_RSS_NONFRAG_IPV6_OTHER; + + if ((rssHF & RSS_L2_PAYLOAD) != 0) + dpdkRssHF |= ETH_RSS_L2_PAYLOAD; + + if ((rssHF & RSS_IPV6_EX) != 0) + dpdkRssHF |= ETH_RSS_IPV6_EX; + + if ((rssHF & RSS_IPV6_TCP_EX) != 0) + dpdkRssHF |= ETH_RSS_IPV6_TCP_EX; + + if ((rssHF & RSS_IPV6_UDP_EX) != 0) + dpdkRssHF |= ETH_RSS_IPV6_UDP_EX; + + if ((rssHF & RSS_PORT) != 0) + dpdkRssHF |= ETH_RSS_PORT; + + if ((rssHF & RSS_VXLAN) != 0) + dpdkRssHF |= ETH_RSS_VXLAN; + + if ((rssHF & RSS_GENEVE) != 0) + dpdkRssHF |= ETH_RSS_GENEVE; + + if ((rssHF & RSS_NVGRE) != 0) + dpdkRssHF |= ETH_RSS_NVGRE; + + return dpdkRssHF; +} + +uint64_t DpdkDevice::convertDpdkRssHfToRssHf(uint64_t dpdkRssHF) const +{ + uint64_t rssHF = 0; + + if ((dpdkRssHF & ETH_RSS_IPV4) != 0) + rssHF |= RSS_IPV4; + + if ((dpdkRssHF & ETH_RSS_FRAG_IPV4) != 0) + rssHF |= RSS_FRAG_IPV4; + + if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV4_TCP) != 0) + rssHF |= RSS_NONFRAG_IPV4_TCP; + + if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV4_UDP) != 0) + rssHF |= RSS_NONFRAG_IPV4_UDP; + + if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV4_SCTP) != 0) + rssHF |= RSS_NONFRAG_IPV4_SCTP; + + if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV4_OTHER) != 0) + rssHF |= RSS_NONFRAG_IPV4_OTHER; + + if ((dpdkRssHF & ETH_RSS_IPV6) != 0) + rssHF |= RSS_IPV6; + + if ((dpdkRssHF & ETH_RSS_FRAG_IPV6) != 0) + rssHF |= RSS_FRAG_IPV6; + + if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV6_TCP) != 0) + rssHF |= RSS_NONFRAG_IPV6_TCP; + + if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV6_UDP) != 0) + rssHF |= RSS_NONFRAG_IPV6_UDP; + + if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV6_SCTP) != 0) + rssHF |= RSS_NONFRAG_IPV6_SCTP; + + if ((dpdkRssHF & ETH_RSS_NONFRAG_IPV6_OTHER) != 0) + rssHF |= RSS_NONFRAG_IPV6_OTHER; + + if ((dpdkRssHF & ETH_RSS_L2_PAYLOAD) != 0) + rssHF |= RSS_L2_PAYLOAD; + + if ((dpdkRssHF & ETH_RSS_IPV6_EX) != 0) + rssHF |= RSS_IPV6_EX; + + if ((dpdkRssHF & ETH_RSS_IPV6_TCP_EX) != 0) + rssHF |= RSS_IPV6_TCP_EX; + + if ((dpdkRssHF & ETH_RSS_IPV6_UDP_EX) != 0) + rssHF |= RSS_IPV6_UDP_EX; + + if ((dpdkRssHF & ETH_RSS_PORT) != 0) + rssHF |= RSS_PORT; + + if ((dpdkRssHF & ETH_RSS_VXLAN) != 0) + rssHF |= RSS_VXLAN; + + if ((dpdkRssHF & ETH_RSS_GENEVE) != 0) + rssHF |= RSS_GENEVE; + + if ((dpdkRssHF & ETH_RSS_NVGRE) != 0) + rssHF |= RSS_NVGRE; + + return rssHF; +} + +bool DpdkDevice::isDeviceSupportRssHashFunction(DpdkRssHashFunction rssHF) const +{ + return isDeviceSupportRssHashFunction((uint64_t)rssHF); +} + +bool DpdkDevice::isDeviceSupportRssHashFunction(uint64_t rssHFMask) const +{ + uint64_t dpdkRssHF = convertRssHfToDpdkRssHf(rssHFMask); + + rte_eth_dev_info devInfo; + rte_eth_dev_info_get(m_Id, &devInfo); + + return ((devInfo.flow_type_rss_offloads & dpdkRssHF) == dpdkRssHF); +} + +uint64_t DpdkDevice::getSupportedRssHashFunctions() const +{ + rte_eth_dev_info devInfo; + rte_eth_dev_info_get(m_Id, &devInfo); + + return convertDpdkRssHfToRssHf(devInfo.flow_type_rss_offloads); +} + +uint64_t DpdkDevice::getConfiguredRssHashFunction() const +{ + if (m_Config.rssHashFunction == static_cast(RSS_DEFAULT)) + { + if (m_PMDType == PMD_I40E || m_PMDType == PMD_I40EVF) + { + return RSS_NONFRAG_IPV4_TCP | RSS_NONFRAG_IPV4_UDP | RSS_NONFRAG_IPV4_OTHER | RSS_FRAG_IPV4 | RSS_NONFRAG_IPV6_TCP | RSS_NONFRAG_IPV6_UDP | RSS_NONFRAG_IPV6_OTHER | RSS_FRAG_IPV6; + } + else + { + return RSS_IPV4 | RSS_IPV6; + } + + } + + if (m_Config.rssHashFunction == static_cast(RSS_ALL_SUPPORTED)) + { + return getSupportedRssHashFunctions(); + } + + return m_Config.rssHashFunction; +} + +std::vector DpdkDevice::rssHashFunctionMaskToString(uint64_t rssHFMask) const +{ + std::vector result = std::vector(); + + if (rssHFMask == RSS_NONE) + { + result.push_back("RSS_NONE"); + return result; + } + + if ((rssHFMask & RSS_IPV4) != 0) + result.push_back("RSS_IPV4"); + + if ((rssHFMask & RSS_FRAG_IPV4) != 0) + result.push_back("RSS_FRAG_IPV4"); + + if ((rssHFMask & RSS_NONFRAG_IPV4_TCP) != 0) + result.push_back("RSS_NONFRAG_IPV4_TCP"); + + if ((rssHFMask & RSS_NONFRAG_IPV4_UDP) != 0) + result.push_back("RSS_NONFRAG_IPV4_UDP"); + + if ((rssHFMask & RSS_NONFRAG_IPV4_SCTP) != 0) + result.push_back("RSS_NONFRAG_IPV4_SCTP"); + + if ((rssHFMask & RSS_NONFRAG_IPV4_OTHER) != 0) + result.push_back("RSS_NONFRAG_IPV4_OTHER"); + + if ((rssHFMask & RSS_IPV6) != 0) + result.push_back("RSS_IPV6"); + + if ((rssHFMask & RSS_FRAG_IPV6) != 0) + result.push_back("RSS_FRAG_IPV6"); + + if ((rssHFMask & RSS_NONFRAG_IPV6_TCP) != 0) + result.push_back("RSS_NONFRAG_IPV6_TCP"); + + if ((rssHFMask & RSS_NONFRAG_IPV6_UDP) != 0) + result.push_back("RSS_NONFRAG_IPV6_UDP"); + + if ((rssHFMask & RSS_NONFRAG_IPV6_SCTP) != 0) + result.push_back("RSS_NONFRAG_IPV6_SCTP"); + + if ((rssHFMask & RSS_NONFRAG_IPV6_OTHER) != 0) + result.push_back("RSS_NONFRAG_IPV6_OTHER"); + + if ((rssHFMask & RSS_L2_PAYLOAD) != 0) + result.push_back("RSS_L2_PAYLOAD"); + + if ((rssHFMask & RSS_IPV6_EX) != 0) + result.push_back("RSS_IPV6_EX"); + + if ((rssHFMask & RSS_IPV6_TCP_EX) != 0) + result.push_back("RSS_IPV6_TCP_EX"); + + if ((rssHFMask & RSS_IPV6_UDP_EX) != 0) + result.push_back("RSS_IPV6_UDP_EX"); + + if ((rssHFMask & RSS_PORT) != 0) + result.push_back("RSS_PORT"); + + if ((rssHFMask & RSS_VXLAN) != 0) + result.push_back("RSS_VXLAN"); + + if ((rssHFMask & RSS_GENEVE) != 0) + result.push_back("RSS_GENEVE"); + + if ((rssHFMask & RSS_NVGRE) != 0) + result.push_back("RSS_NVGRE"); + + return result; +} + + +} // namespace pcpp + +#endif /* USE_DPDK */ diff --git a/Pcap++/src/DpdkDeviceList.cpp b/Pcap++/src/DpdkDeviceList.cpp index 4034fd1b4f..4598d6c081 100644 --- a/Pcap++/src/DpdkDeviceList.cpp +++ b/Pcap++/src/DpdkDeviceList.cpp @@ -1,424 +1,424 @@ -#ifdef USE_DPDK - -#define LOG_MODULE PcapLogModuleDpdkDevice - -#define __STDC_LIMIT_MACROS -#define __STDC_FORMAT_MACROS - -#include "DpdkDeviceList.h" -#include "Logger.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#if (RTE_VER_YEAR < 21) || (RTE_VER_YEAR == 21 && RTE_VER_MONTH < 11) -#define GET_MASTER_CORE rte_get_master_lcore -#define MASTER_LCORE "--master-lcore" -#else -#define GET_MASTER_CORE rte_get_main_lcore -#define MASTER_LCORE "--main-lcore" -#endif - -namespace pcpp -{ - -bool DpdkDeviceList::m_IsDpdkInitialized = false; -CoreMask DpdkDeviceList::m_CoreMask = 0; -uint32_t DpdkDeviceList::m_MBufPoolSizePerDevice = 0; - -DpdkDeviceList::DpdkDeviceList() -{ - m_IsInitialized = false; -} - -DpdkDeviceList::~DpdkDeviceList() -{ - for (std::vector::iterator iter = m_DpdkDeviceList.begin(); iter != m_DpdkDeviceList.end(); iter++) - { - delete (*iter); - } - - m_DpdkDeviceList.clear(); -} - -bool DpdkDeviceList::initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice, uint8_t masterCore, uint32_t initDpdkArgc, char **initDpdkArgv, const std::string& appName) -{ - char **initDpdkArgvBuffer; - - if (m_IsDpdkInitialized) - { - if (coreMask == m_CoreMask) - return true; - else - { - PCPP_LOG_ERROR("Trying to re-initialize DPDK with a different core mask"); - return false; - } - } - - if (!verifyHugePagesAndDpdkDriver()) - { - return false; - } - - // verify mBufPoolSizePerDevice is power of 2 minus 1 - bool isPoolSizePowerOfTwoMinusOne = !(mBufPoolSizePerDevice == 0) && !((mBufPoolSizePerDevice+1) & (mBufPoolSizePerDevice)); - if (!isPoolSizePowerOfTwoMinusOne) - { - PCPP_LOG_ERROR("mBuf pool size must be a power of two minus one: n = (2^q - 1). It's currently: " << mBufPoolSizePerDevice); - return false; - } - - - std::stringstream dpdkParamsStream; - dpdkParamsStream << appName << " "; - dpdkParamsStream << "-n "; - dpdkParamsStream << "2 "; - dpdkParamsStream << "-c "; - dpdkParamsStream << "0x" << std::hex << std::setw(2) << std::setfill('0') << coreMask << " "; - dpdkParamsStream << MASTER_LCORE << " "; - dpdkParamsStream << (int)masterCore << " "; - - uint32_t i = 0; - while (i < initDpdkArgc && initDpdkArgv[i] != NULL) - { - dpdkParamsStream << initDpdkArgv[i] << " "; - i++; - } - - // Should be equal to the number of static params - initDpdkArgc += 7; - std::string dpdkParamsArray[initDpdkArgc]; - initDpdkArgvBuffer = new char*[initDpdkArgc]; - i = 0; - while (dpdkParamsStream.good() && i < initDpdkArgc) - { - dpdkParamsStream >> dpdkParamsArray[i]; - initDpdkArgvBuffer[i] = new char[dpdkParamsArray[i].length() + 1]; - strcpy(initDpdkArgvBuffer[i], dpdkParamsArray[i].c_str()); - i++; - } - - char* lastParam = initDpdkArgvBuffer[i-1]; - - for (i = 0; i < initDpdkArgc; i++) - { - PCPP_LOG_DEBUG("DPDK initialization params: " << initDpdkArgvBuffer[i]); - } - - optind = 1; - // init the EAL - int ret = rte_eal_init(initDpdkArgc, (char**)initDpdkArgvBuffer); - if (ret < 0) - { - PCPP_LOG_ERROR("failed to init the DPDK EAL"); - return false; - } - - for (i = 0; i < initDpdkArgc-1; i++) - { - delete [] initDpdkArgvBuffer[i]; - } - delete [] lastParam; - - delete [] initDpdkArgvBuffer; - - m_CoreMask = coreMask; - m_IsDpdkInitialized = true; - - m_MBufPoolSizePerDevice = mBufPoolSizePerDevice; - DpdkDeviceList::getInstance().setDpdkLogLevel(Logger::Info); - return DpdkDeviceList::getInstance().initDpdkDevices(m_MBufPoolSizePerDevice); -} - -bool DpdkDeviceList::initDpdkDevices(uint32_t mBufPoolSizePerDevice) -{ - if (!m_IsDpdkInitialized) - { - PCPP_LOG_ERROR("DPDK is not initialized!! Please call DpdkDeviceList::initDpdk(coreMask, mBufPoolSizePerDevice) before start using DPDK devices"); - return false; - } - - if (m_IsInitialized) - return true; - -#if (RTE_VER_YEAR < 18) || (RTE_VER_YEAR == 18 && RTE_VER_MONTH < 5) - int numOfPorts = (int)rte_eth_dev_count(); -#else - int numOfPorts = (int)rte_eth_dev_count_avail(); -#endif - - if (numOfPorts <= 0) - { - PCPP_LOG_ERROR("Zero DPDK ports are initialized. Something went wrong while initializing DPDK"); - return false; - } - - PCPP_LOG_DEBUG("Found " << numOfPorts << " DPDK ports. Constructing DpdkDevice for each one"); - - // Initialize a DpdkDevice per port - for (int i = 0; i < numOfPorts; i++) - { - DpdkDevice* newDevice = new DpdkDevice(i, mBufPoolSizePerDevice); - PCPP_LOG_DEBUG("DpdkDevice #" << i << ": Name='" << newDevice->getDeviceName() << "', PCI-slot='" << newDevice->getPciAddress() << "', PMD='" << newDevice->getPMDName() << "', MAC Addr='" << newDevice->getMacAddress() << "'"); - m_DpdkDeviceList.push_back(newDevice); - } - - m_IsInitialized = true; - return true; -} - -DpdkDevice* DpdkDeviceList::getDeviceByPort(int portId) const -{ - if (!isInitialized()) - { - PCPP_LOG_ERROR("DpdkDeviceList not initialized"); - return NULL; - } - - if ((uint32_t)portId >= m_DpdkDeviceList.size()) - { - return NULL; - } - - return m_DpdkDeviceList.at(portId); -} - -DpdkDevice* DpdkDeviceList::getDeviceByPciAddress(const std::string& pciAddr) const -{ - if (!isInitialized()) - { - PCPP_LOG_ERROR("DpdkDeviceList not initialized"); - return NULL; - } - - for (std::vector::const_iterator iter = m_DpdkDeviceList.begin(); iter != m_DpdkDeviceList.end(); iter++) - { - if ((*iter)->getPciAddress() == pciAddr) - return (*iter); - } - - return NULL; -} - -bool DpdkDeviceList::verifyHugePagesAndDpdkDriver() -{ - std::string execResult = executeShellCommand("cat /proc/meminfo | grep -s HugePages_Total | awk '{print $2}'"); - // trim '\n' at the end - execResult.erase(std::remove(execResult.begin(), execResult.end(), '\n'), execResult.end()); - - // convert the result to long - char* endPtr; - long totalHugePages = strtol(execResult.c_str(), &endPtr, 10); - - PCPP_LOG_DEBUG("Total number of huge-pages is " << totalHugePages); - - if (totalHugePages <= 0) - { - PCPP_LOG_ERROR("Huge pages aren't set, DPDK cannot be initialized. Please run /setup_dpdk.sh"); - return false; - } - - execResult = executeShellCommand("lsmod | grep -s igb_uio"); - if (execResult == "") - { - execResult = executeShellCommand("modinfo -d uio_pci_generic"); - if (execResult.find("ERROR") != std::string::npos) - { - execResult = executeShellCommand("modinfo -d vfio-pci"); - if (execResult.find("ERROR") != std::string::npos) - { - PCPP_LOG_ERROR("None of igb_uio, uio_pci_generic, vfio-pci kernel modules are loaded so DPDK cannot be initialized. Please run /setup_dpdk.sh"); - return false; - } - else - { - PCPP_LOG_DEBUG("vfio-pci module is loaded"); - } - } - else - { - PCPP_LOG_DEBUG("uio_pci_generic module is loaded"); - } - } - else - PCPP_LOG_DEBUG("igb_uio driver is loaded"); - - return true; -} - -SystemCore DpdkDeviceList::getDpdkMasterCore() const -{ - return SystemCores::IdToSystemCore[GET_MASTER_CORE()]; -} - -void DpdkDeviceList::setDpdkLogLevel(Logger::LogLevel logLevel) -{ -#if (RTE_VER_YEAR > 17) || (RTE_VER_YEAR == 17 && RTE_VER_MONTH >= 11) - if (logLevel == Logger::Info) - rte_log_set_global_level(RTE_LOG_NOTICE); - else // logLevel == Logger::Debug - rte_log_set_global_level(RTE_LOG_DEBUG); -#else - if (logLevel == Logger::Info) - rte_set_log_level(RTE_LOG_NOTICE); - else // logLevel == Logger::Debug - rte_set_log_level(RTE_LOG_DEBUG); -#endif -} - -Logger::LogLevel DpdkDeviceList::getDpdkLogLevel() const -{ -#if (RTE_VER_YEAR > 17) || (RTE_VER_YEAR == 17 && RTE_VER_MONTH >= 11) - if (rte_log_get_global_level() <= RTE_LOG_NOTICE) -#else - if (rte_get_log_level() <= RTE_LOG_NOTICE) -#endif - return Logger::Info; - else - return Logger::Debug; -} - -bool DpdkDeviceList::writeDpdkLogToFile(FILE* logFile) -{ - return (rte_openlog_stream(logFile) == 0); -} - -int DpdkDeviceList::dpdkWorkerThreadStart(void *ptr) -{ - DpdkWorkerThread* workerThread = (DpdkWorkerThread*)ptr; - workerThread->run(rte_lcore_id()); - return 0; -} - -bool DpdkDeviceList::startDpdkWorkerThreads(CoreMask coreMask, std::vector& workerThreadsVec) -{ - if (!isInitialized()) - { - PCPP_LOG_ERROR("DpdkDeviceList not initialized"); - return false; - } - - CoreMask tempCoreMask = coreMask; - size_t numOfCoresInMask = 0; - int coreNum = 0; - while (tempCoreMask > 0) - { - if (tempCoreMask & 1) - { - if (!rte_lcore_is_enabled(coreNum)) - { - PCPP_LOG_ERROR("Trying to use core #" << coreNum << " which isn't initialized by DPDK"); - return false; - } - - numOfCoresInMask++; - } - tempCoreMask = tempCoreMask >> 1; - coreNum++; - } - - if (numOfCoresInMask == 0) - { - PCPP_LOG_ERROR("Number of cores in mask is 0"); - return false; - } - - if (numOfCoresInMask != workerThreadsVec.size()) - { - PCPP_LOG_ERROR("Number of cores in core mask different from workerThreadsVec size"); - return false; - } - - if (coreMask & getDpdkMasterCore().Mask) - { - PCPP_LOG_ERROR("Cannot run worker thread on DPDK master core"); - return false; - } - - m_WorkerThreads.clear(); - uint32_t index = 0; - std::vector::iterator iter = workerThreadsVec.begin(); - while (iter != workerThreadsVec.end()) - { - SystemCore core = SystemCores::IdToSystemCore[index]; - if (!(coreMask & core.Mask)) - { - index++; - continue; - } - - int err = rte_eal_remote_launch(dpdkWorkerThreadStart, *iter, core.Id); - if (err != 0) - { - for (std::vector::iterator iter2 = workerThreadsVec.begin(); iter2 != iter; iter2++) - { - (*iter)->stop(); - rte_eal_wait_lcore((*iter)->getCoreId()); - PCPP_LOG_DEBUG("Thread on core [" << (*iter)->getCoreId() << "] stopped"); - } - PCPP_LOG_ERROR("Cannot create worker thread #" << core.Id << ". Error was: [" << strerror(err) << "]"); - return false; - } - m_WorkerThreads.push_back(*iter); - - index++; - iter++; - } - - return true; -} - -void DpdkDeviceList::stopDpdkWorkerThreads() -{ - if (m_WorkerThreads.empty()) - { - PCPP_LOG_ERROR("No worker threads were set"); - return; - } - - for (std::vector::iterator iter = m_WorkerThreads.begin(); iter != m_WorkerThreads.end(); iter++) - { - (*iter)->stop(); - rte_eal_wait_lcore((*iter)->getCoreId()); - PCPP_LOG_DEBUG("Thread on core [" << (*iter)->getCoreId() << "] stopped"); - } - - m_WorkerThreads.clear(); - std::vector(m_WorkerThreads).swap(m_WorkerThreads); - - PCPP_LOG_DEBUG("All worker threads stopped"); -} - -} // namespace pcpp - -#endif /* USE_DPDK */ +#ifdef USE_DPDK + +#define LOG_MODULE PcapLogModuleDpdkDevice + +#define __STDC_LIMIT_MACROS +#define __STDC_FORMAT_MACROS + +#include "DpdkDeviceList.h" +#include "Logger.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if (RTE_VER_YEAR < 21) || (RTE_VER_YEAR == 21 && RTE_VER_MONTH < 11) +#define GET_MASTER_CORE rte_get_master_lcore +#define MASTER_LCORE "--master-lcore" +#else +#define GET_MASTER_CORE rte_get_main_lcore +#define MASTER_LCORE "--main-lcore" +#endif + +namespace pcpp +{ + +bool DpdkDeviceList::m_IsDpdkInitialized = false; +CoreMask DpdkDeviceList::m_CoreMask = 0; +uint32_t DpdkDeviceList::m_MBufPoolSizePerDevice = 0; + +DpdkDeviceList::DpdkDeviceList() +{ + m_IsInitialized = false; +} + +DpdkDeviceList::~DpdkDeviceList() +{ + for (std::vector::iterator iter = m_DpdkDeviceList.begin(); iter != m_DpdkDeviceList.end(); iter++) + { + delete (*iter); + } + + m_DpdkDeviceList.clear(); +} + +bool DpdkDeviceList::initDpdk(CoreMask coreMask, uint32_t mBufPoolSizePerDevice, uint8_t masterCore, uint32_t initDpdkArgc, char **initDpdkArgv, const std::string& appName) +{ + char **initDpdkArgvBuffer; + + if (m_IsDpdkInitialized) + { + if (coreMask == m_CoreMask) + return true; + else + { + PCPP_LOG_ERROR("Trying to re-initialize DPDK with a different core mask"); + return false; + } + } + + if (!verifyHugePagesAndDpdkDriver()) + { + return false; + } + + // verify mBufPoolSizePerDevice is power of 2 minus 1 + bool isPoolSizePowerOfTwoMinusOne = !(mBufPoolSizePerDevice == 0) && !((mBufPoolSizePerDevice+1) & (mBufPoolSizePerDevice)); + if (!isPoolSizePowerOfTwoMinusOne) + { + PCPP_LOG_ERROR("mBuf pool size must be a power of two minus one: n = (2^q - 1). It's currently: " << mBufPoolSizePerDevice); + return false; + } + + + std::stringstream dpdkParamsStream; + dpdkParamsStream << appName << " "; + dpdkParamsStream << "-n "; + dpdkParamsStream << "2 "; + dpdkParamsStream << "-c "; + dpdkParamsStream << "0x" << std::hex << std::setw(2) << std::setfill('0') << coreMask << " "; + dpdkParamsStream << MASTER_LCORE << " "; + dpdkParamsStream << (int)masterCore << " "; + + uint32_t i = 0; + while (i < initDpdkArgc && initDpdkArgv[i] != NULL) + { + dpdkParamsStream << initDpdkArgv[i] << " "; + i++; + } + + // Should be equal to the number of static params + initDpdkArgc += 7; + std::string dpdkParamsArray[initDpdkArgc]; + initDpdkArgvBuffer = new char*[initDpdkArgc]; + i = 0; + while (dpdkParamsStream.good() && i < initDpdkArgc) + { + dpdkParamsStream >> dpdkParamsArray[i]; + initDpdkArgvBuffer[i] = new char[dpdkParamsArray[i].length() + 1]; + strcpy(initDpdkArgvBuffer[i], dpdkParamsArray[i].c_str()); + i++; + } + + char* lastParam = initDpdkArgvBuffer[i-1]; + + for (i = 0; i < initDpdkArgc; i++) + { + PCPP_LOG_DEBUG("DPDK initialization params: " << initDpdkArgvBuffer[i]); + } + + optind = 1; + // init the EAL + int ret = rte_eal_init(initDpdkArgc, (char**)initDpdkArgvBuffer); + if (ret < 0) + { + PCPP_LOG_ERROR("failed to init the DPDK EAL"); + return false; + } + + for (i = 0; i < initDpdkArgc-1; i++) + { + delete [] initDpdkArgvBuffer[i]; + } + delete [] lastParam; + + delete [] initDpdkArgvBuffer; + + m_CoreMask = coreMask; + m_IsDpdkInitialized = true; + + m_MBufPoolSizePerDevice = mBufPoolSizePerDevice; + DpdkDeviceList::getInstance().setDpdkLogLevel(Logger::Info); + return DpdkDeviceList::getInstance().initDpdkDevices(m_MBufPoolSizePerDevice); +} + +bool DpdkDeviceList::initDpdkDevices(uint32_t mBufPoolSizePerDevice) +{ + if (!m_IsDpdkInitialized) + { + PCPP_LOG_ERROR("DPDK is not initialized!! Please call DpdkDeviceList::initDpdk(coreMask, mBufPoolSizePerDevice) before start using DPDK devices"); + return false; + } + + if (m_IsInitialized) + return true; + +#if (RTE_VER_YEAR < 18) || (RTE_VER_YEAR == 18 && RTE_VER_MONTH < 5) + int numOfPorts = (int)rte_eth_dev_count(); +#else + int numOfPorts = (int)rte_eth_dev_count_avail(); +#endif + + if (numOfPorts <= 0) + { + PCPP_LOG_ERROR("Zero DPDK ports are initialized. Something went wrong while initializing DPDK"); + return false; + } + + PCPP_LOG_DEBUG("Found " << numOfPorts << " DPDK ports. Constructing DpdkDevice for each one"); + + // Initialize a DpdkDevice per port + for (int i = 0; i < numOfPorts; i++) + { + DpdkDevice* newDevice = new DpdkDevice(i, mBufPoolSizePerDevice); + PCPP_LOG_DEBUG("DpdkDevice #" << i << ": Name='" << newDevice->getDeviceName() << "', PCI-slot='" << newDevice->getPciAddress() << "', PMD='" << newDevice->getPMDName() << "', MAC Addr='" << newDevice->getMacAddress() << "'"); + m_DpdkDeviceList.push_back(newDevice); + } + + m_IsInitialized = true; + return true; +} + +DpdkDevice* DpdkDeviceList::getDeviceByPort(int portId) const +{ + if (!isInitialized()) + { + PCPP_LOG_ERROR("DpdkDeviceList not initialized"); + return NULL; + } + + if ((uint32_t)portId >= m_DpdkDeviceList.size()) + { + return NULL; + } + + return m_DpdkDeviceList.at(portId); +} + +DpdkDevice* DpdkDeviceList::getDeviceByPciAddress(const std::string& pciAddr) const +{ + if (!isInitialized()) + { + PCPP_LOG_ERROR("DpdkDeviceList not initialized"); + return NULL; + } + + for (std::vector::const_iterator iter = m_DpdkDeviceList.begin(); iter != m_DpdkDeviceList.end(); iter++) + { + if ((*iter)->getPciAddress() == pciAddr) + return (*iter); + } + + return NULL; +} + +bool DpdkDeviceList::verifyHugePagesAndDpdkDriver() +{ + std::string execResult = executeShellCommand("cat /proc/meminfo | grep -s HugePages_Total | awk '{print $2}'"); + // trim '\n' at the end + execResult.erase(std::remove(execResult.begin(), execResult.end(), '\n'), execResult.end()); + + // convert the result to long + char* endPtr; + long totalHugePages = strtol(execResult.c_str(), &endPtr, 10); + + PCPP_LOG_DEBUG("Total number of huge-pages is " << totalHugePages); + + if (totalHugePages <= 0) + { + PCPP_LOG_ERROR("Huge pages aren't set, DPDK cannot be initialized. Please run /setup_dpdk.sh"); + return false; + } + + execResult = executeShellCommand("lsmod | grep -s igb_uio"); + if (execResult == "") + { + execResult = executeShellCommand("modinfo -d uio_pci_generic"); + if (execResult.find("ERROR") != std::string::npos) + { + execResult = executeShellCommand("modinfo -d vfio-pci"); + if (execResult.find("ERROR") != std::string::npos) + { + PCPP_LOG_ERROR("None of igb_uio, uio_pci_generic, vfio-pci kernel modules are loaded so DPDK cannot be initialized. Please run /setup_dpdk.sh"); + return false; + } + else + { + PCPP_LOG_DEBUG("vfio-pci module is loaded"); + } + } + else + { + PCPP_LOG_DEBUG("uio_pci_generic module is loaded"); + } + } + else + PCPP_LOG_DEBUG("igb_uio driver is loaded"); + + return true; +} + +SystemCore DpdkDeviceList::getDpdkMasterCore() const +{ + return SystemCores::IdToSystemCore[GET_MASTER_CORE()]; +} + +void DpdkDeviceList::setDpdkLogLevel(Logger::LogLevel logLevel) +{ +#if (RTE_VER_YEAR > 17) || (RTE_VER_YEAR == 17 && RTE_VER_MONTH >= 11) + if (logLevel == Logger::Info) + rte_log_set_global_level(RTE_LOG_NOTICE); + else // logLevel == Logger::Debug + rte_log_set_global_level(RTE_LOG_DEBUG); +#else + if (logLevel == Logger::Info) + rte_set_log_level(RTE_LOG_NOTICE); + else // logLevel == Logger::Debug + rte_set_log_level(RTE_LOG_DEBUG); +#endif +} + +Logger::LogLevel DpdkDeviceList::getDpdkLogLevel() const +{ +#if (RTE_VER_YEAR > 17) || (RTE_VER_YEAR == 17 && RTE_VER_MONTH >= 11) + if (rte_log_get_global_level() <= RTE_LOG_NOTICE) +#else + if (rte_get_log_level() <= RTE_LOG_NOTICE) +#endif + return Logger::Info; + else + return Logger::Debug; +} + +bool DpdkDeviceList::writeDpdkLogToFile(FILE* logFile) +{ + return (rte_openlog_stream(logFile) == 0); +} + +int DpdkDeviceList::dpdkWorkerThreadStart(void *ptr) +{ + DpdkWorkerThread* workerThread = (DpdkWorkerThread*)ptr; + workerThread->run(rte_lcore_id()); + return 0; +} + +bool DpdkDeviceList::startDpdkWorkerThreads(CoreMask coreMask, std::vector& workerThreadsVec) +{ + if (!isInitialized()) + { + PCPP_LOG_ERROR("DpdkDeviceList not initialized"); + return false; + } + + CoreMask tempCoreMask = coreMask; + size_t numOfCoresInMask = 0; + int coreNum = 0; + while (tempCoreMask > 0) + { + if (tempCoreMask & 1) + { + if (!rte_lcore_is_enabled(coreNum)) + { + PCPP_LOG_ERROR("Trying to use core #" << coreNum << " which isn't initialized by DPDK"); + return false; + } + + numOfCoresInMask++; + } + tempCoreMask = tempCoreMask >> 1; + coreNum++; + } + + if (numOfCoresInMask == 0) + { + PCPP_LOG_ERROR("Number of cores in mask is 0"); + return false; + } + + if (numOfCoresInMask != workerThreadsVec.size()) + { + PCPP_LOG_ERROR("Number of cores in core mask different from workerThreadsVec size"); + return false; + } + + if (coreMask & getDpdkMasterCore().Mask) + { + PCPP_LOG_ERROR("Cannot run worker thread on DPDK master core"); + return false; + } + + m_WorkerThreads.clear(); + uint32_t index = 0; + std::vector::iterator iter = workerThreadsVec.begin(); + while (iter != workerThreadsVec.end()) + { + SystemCore core = SystemCores::IdToSystemCore[index]; + if (!(coreMask & core.Mask)) + { + index++; + continue; + } + + int err = rte_eal_remote_launch(dpdkWorkerThreadStart, *iter, core.Id); + if (err != 0) + { + for (std::vector::iterator iter2 = workerThreadsVec.begin(); iter2 != iter; iter2++) + { + (*iter)->stop(); + rte_eal_wait_lcore((*iter)->getCoreId()); + PCPP_LOG_DEBUG("Thread on core [" << (*iter)->getCoreId() << "] stopped"); + } + PCPP_LOG_ERROR("Cannot create worker thread #" << core.Id << ". Error was: [" << strerror(err) << "]"); + return false; + } + m_WorkerThreads.push_back(*iter); + + index++; + iter++; + } + + return true; +} + +void DpdkDeviceList::stopDpdkWorkerThreads() +{ + if (m_WorkerThreads.empty()) + { + PCPP_LOG_ERROR("No worker threads were set"); + return; + } + + for (std::vector::iterator iter = m_WorkerThreads.begin(); iter != m_WorkerThreads.end(); iter++) + { + (*iter)->stop(); + rte_eal_wait_lcore((*iter)->getCoreId()); + PCPP_LOG_DEBUG("Thread on core [" << (*iter)->getCoreId() << "] stopped"); + } + + m_WorkerThreads.clear(); + std::vector(m_WorkerThreads).swap(m_WorkerThreads); + + PCPP_LOG_DEBUG("All worker threads stopped"); +} + +} // namespace pcpp + +#endif /* USE_DPDK */ diff --git a/Pcap++/src/NetworkUtils.cpp b/Pcap++/src/NetworkUtils.cpp index 3258c28ec6..3452e33560 100644 --- a/Pcap++/src/NetworkUtils.cpp +++ b/Pcap++/src/NetworkUtils.cpp @@ -1,447 +1,447 @@ -#define LOG_MODULE NetworkUtils - -#include -#include -#include -#include -#include "Logger.h" -#include "Packet.h" -#include "EthLayer.h" -#include "ArpLayer.h" -#include "IPv4Layer.h" -#include "UdpLayer.h" -#include "DnsLayer.h" -#include "PcapFilter.h" -#include "NetworkUtils.h" -#include "EndianPortable.h" -#ifdef _MSC_VER -#include "SystemUtils.h" -#endif -#ifndef ETIMEDOUT -#define ETIMEDOUT 10060 -#endif - -#define DNS_PORT 53 - - -namespace pcpp -{ - -const int NetworkUtils::DefaultTimeout = 5; - - -struct ArpingReceivedData -{ - std::mutex &mutex; - std::condition_variable &cond; - IPv4Address ipAddr; - clock_t start; - MacAddress result; - double arpResponseTime; -}; - - -static void arpPacketReceived(RawPacket* rawPacket, PcapLiveDevice* device, void* userCookie) -{ - // extract timestamp of packet - clock_t receiveTime = clock(); - - // get the data from the main thread - ArpingReceivedData* data = (ArpingReceivedData*)userCookie; - - // parse the response packet - Packet packet(rawPacket); - - // verify that it's an ARP packet (although it must be because I set an ARP reply filter on the interface) - if (!packet.isPacketOfType(ARP)) - return; - - // extract the ARP layer from the packet - ArpLayer* arpReplyLayer = packet.getLayerOfType(true); // lookup in reverse order - if (arpReplyLayer == nullptr) - return; - - // verify it's the right ARP response - if (arpReplyLayer->getArpHeader()->hardwareType != htobe16(1) /* Ethernet */ - || arpReplyLayer->getArpHeader()->protocolType != htobe16(PCPP_ETHERTYPE_IP)) - return; - - // verify the ARP response is the response for out request (and not some arbitrary ARP response) - if (arpReplyLayer->getSenderIpAddr() != data->ipAddr) - return; - - // measure response time - double diffticks = receiveTime-data->start; - double diffms = (diffticks*1000)/CLOCKS_PER_SEC; - - data->arpResponseTime = diffms; - data->result = arpReplyLayer->getSenderMacAddress(); - - // signal the main thread the ARP reply was received - data->cond.notify_one(); -} - - -MacAddress NetworkUtils::getMacAddress(IPv4Address ipAddr, PcapLiveDevice* device, double& arpResponseTimeMS, - MacAddress sourceMac, IPv4Address sourceIP, int arpTimeout) const -{ - MacAddress result = MacAddress::Zero; - - // open the device if not already opened - bool closeDeviceAtTheEnd = false; - if (!device->isOpened()) - { - closeDeviceAtTheEnd = true; - if (!device->open()) - { - PCPP_LOG_ERROR("Cannot open device"); - return result; - } - } - - if (sourceMac == MacAddress::Zero) - sourceMac = device->getMacAddress(); - - if (sourceIP == IPv4Address::Zero) - sourceIP = device->getIPv4Address(); - - if (arpTimeout <= 0) - arpTimeout = NetworkUtils::DefaultTimeout; - - // create an ARP request from sourceMac and sourceIP and ask for target IP - - Packet arpRequest(100); - - MacAddress destMac(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); - EthLayer ethLayer(sourceMac, destMac); - - ArpLayer arpLayer(ARP_REQUEST, sourceMac, destMac, sourceIP, ipAddr); - - if (!arpRequest.addLayer(ðLayer)) - { - PCPP_LOG_ERROR("Couldn't build Eth layer for ARP request"); - return result; - } - - if (!arpRequest.addLayer(&arpLayer)) - { - PCPP_LOG_ERROR("Couldn't build ARP layer for ARP request"); - return result; - } - - arpRequest.computeCalculateFields(); - - // set a filter for the interface to intercept only ARP response packets - ArpFilter arpFilter(ARP_REPLY); - if (!device->setFilter(arpFilter)) - { - PCPP_LOG_ERROR("Couldn't set ARP filter for device"); - return result; - } - - // since packet capture is done on another thread, I use a conditional mutex with timeout to synchronize between the capture - // thread and the main thread. When the capture thread starts running the main thread is blocking on the conditional mutex. - // When the ARP response is captured the capture thread signals the main thread and the main thread stops capturing and continues - // to the next iteration. If a timeout passes and no ARP response is captured, the main thread stops capturing - - std::mutex mutex; - std::condition_variable cond; - - // this is the token that passes between the 2 threads. It contains pointers to the conditional mutex, the target IP for identifying - // the ARP response, the iteration index and a timestamp to calculate the response time - ArpingReceivedData data = { - mutex, - cond, - ipAddr, - clock(), - MacAddress::Zero, - 0 - }; - - struct timeval now; - gettimeofday(&now,nullptr); - - // start capturing. The capture is done on another thread, hence "arpPacketReceived" is running on that thread - device->startCapture(arpPacketReceived, &data); - - // send the ARP request - device->sendPacket(&arpRequest); - - // block on the conditional mutex until capture thread signals or until timeout expires - // cppcheck-suppress localMutex - std::unique_lock lock(mutex); - std::cv_status res = cond.wait_for(lock, std::chrono::seconds(arpTimeout)); - - // stop the capturing thread - device->stopCapture(); - - // check if timeout expired - if (res == std::cv_status::timeout) - { - PCPP_LOG_ERROR("ARP request time out"); - return result; - } - - if (closeDeviceAtTheEnd) - device->close(); - else - device->clearFilter(); - - result = data.result; - arpResponseTimeMS = data.arpResponseTime; - - return result; -} - - - -struct DNSReceivedData -{ - std::mutex &mutex; - std::condition_variable &cond; - std::string hostname; - uint16_t transactionID; - clock_t start; - IPv4Address result; - uint32_t ttl; - double dnsResponseTime; -}; - -static void dnsResponseReceived(RawPacket* rawPacket, PcapLiveDevice* device, void* userCookie) -{ - // extract timestamp of packet - clock_t receiveTime = clock(); - - // get data from the main thread - DNSReceivedData* data = (DNSReceivedData*)userCookie; - - // parse the response packet - Packet packet(rawPacket); - - // verify that it's an DNS packet (although it must be because DNS port filter was set on the interface) - if (!packet.isPacketOfType(DNS)) - return; - - // extract the DNS layer from the packet - DnsLayer* dnsResponseLayer = packet.getLayerOfType(true); // lookup in reverse order - if (dnsResponseLayer == nullptr) - return; - - // verify it's the right DNS response - if (dnsResponseLayer->getDnsHeader()->queryOrResponse != 1 /* DNS response */ - || dnsResponseLayer->getDnsHeader()->numberOfAnswers < htobe16(1) - || dnsResponseLayer->getDnsHeader()->transactionID != htobe16(data->transactionID)) - { - return; - } - - // DNS resolving can be recursive as many DNS responses contain multiple answers with recursive canonical names (CNAME) for - // the hostname. For example: a DNS response for www.a.com can have multiple answers: - //- First with CNAME: www.a.com -> www.b.com - //- Second with CNAME: www.b.com -> www.c.com - //- Third with resolving: www.c.com -> 1.1.1.1 - // So the search must be recursive until an IPv4 resolving is found or until no hostname or canonical name are found (and then return) - - std::string hostToFind = data->hostname; - - DnsResource* dnsAnswer = nullptr; - - while (true) - { - dnsAnswer = dnsResponseLayer->getAnswer(hostToFind, true); - - // if response doesn't contain hostname or cname - return - if (dnsAnswer == nullptr) - { - PCPP_LOG_DEBUG("DNS answer doesn't contain hostname '" << hostToFind << "'"); - return; - } - - DnsType dnsType = dnsAnswer->getDnsType(); - // if answer contains IPv4 resolving - break the loop and return the IP address - if (dnsType == DNS_TYPE_A) - { - PCPP_LOG_DEBUG("Found IPv4 resolving for hostname '" << hostToFind << "'"); - break; - } - // if answer contains a cname - continue to search this cname in the packet - hopefully find the IP resolving - else if (dnsType == DNS_TYPE_CNAME) - { - PCPP_LOG_DEBUG("Got a DNS response for hostname '" << hostToFind << "' with CNAME '" << dnsAnswer->getData()->toString() << "'"); - hostToFind = dnsAnswer->getData()->toString(); - } - // if answer is of type other than A or CNAME (for example AAAA - IPv6) - type is not supported - return - else - { - PCPP_LOG_DEBUG("Got a DNS response with type which is not A or CNAME"); - return; - } - } - // if we got here it means an IPv4 resolving was found - - // measure response time - clock_t diffticks = receiveTime-data->start; - double diffms = (diffticks*1000.0)/CLOCKS_PER_SEC; - - data->dnsResponseTime = diffms; - data->result = dnsAnswer->getData()->castAs()->getIpAddress(); - data->ttl = dnsAnswer->getTTL(); - - // signal the main thread the ARP reply was received - data->cond.notify_one(); -} - - -IPv4Address NetworkUtils::getIPv4Address(const std::string& hostname, PcapLiveDevice* device, double& dnsResponseTimeMS, uint32_t& dnsTTL, - int dnsTimeout, IPv4Address dnsServerIP, IPv4Address gatewayIP) const -{ - IPv4Address result = IPv4Address::Zero; - - // open the device if not already opened - bool closeDeviceAtTheEnd = false; - if (!device->isOpened()) - { - closeDeviceAtTheEnd = true; - if (!device->open()) - { - PCPP_LOG_ERROR("Cannot open device"); - return result; - } - } - - // first - resolve gateway MAC address - - // if gateway IP wasn't provided - try to find the default gateway - if (gatewayIP == IPv4Address::Zero) - { - gatewayIP = device->getDefaultGateway(); - } - - if (!gatewayIP.isValid() || gatewayIP == IPv4Address::Zero) - { - PCPP_LOG_ERROR("Gateway address isn't valid or couldn't find default gateway"); - return result; - } - - // send the ARP request to find gateway MAC address - double arpResTime; - MacAddress gatewayMacAddress = getMacAddress(gatewayIP, device, arpResTime); - - if (gatewayMacAddress == MacAddress::Zero) - { - PCPP_LOG_ERROR("Couldn't resolve gateway MAC address"); - return result; - } - - if (dnsTimeout <= 0) - dnsTimeout = NetworkUtils::DefaultTimeout; - - // validate DNS server IP. If it wasn't provided - set the system-configured DNS server - if (dnsServerIP == IPv4Address::Zero && device->getDnsServers().size() > 0) - { - dnsServerIP = device->getDnsServers().at(0); - } - - if (!dnsServerIP.isValid()) - { - PCPP_LOG_ERROR("DNS server IP isn't valid"); - return result; - } - - // create DNS request - - Packet dnsRequest(100); - MacAddress sourceMac = device->getMacAddress(); - EthLayer ethLayer(sourceMac, gatewayMacAddress, PCPP_ETHERTYPE_IP); - IPv4Layer ipLayer(device->getIPv4Address(), dnsServerIP); - ipLayer.getIPv4Header()->timeToLive = 128; - - // randomize source port to a number >= 10000 - int srcPortLowest = 10000; - int srcPortRange = 65535 - srcPortLowest; - uint16_t srcPort = (rand() % srcPortRange) + srcPortLowest; - UdpLayer udpLayer(srcPort, DNS_PORT); - - // create the DNS request for the hostname - DnsLayer dnsLayer; - - // randomize transaction ID - uint16_t transactionID = rand() % 65535; - dnsLayer.getDnsHeader()->transactionID = htobe16(transactionID); - dnsLayer.addQuery(hostname, DNS_TYPE_A, DNS_CLASS_IN); - - // add all layers to packet - if (!dnsRequest.addLayer(ðLayer) || !dnsRequest.addLayer(&ipLayer) || !dnsRequest.addLayer(&udpLayer) || !dnsRequest.addLayer(&dnsLayer)) - { - PCPP_LOG_ERROR("Couldn't construct DNS query"); - return result; - } - - dnsRequest.computeCalculateFields(); - - // set a DNS response filter on the device - PortFilter dnsResponseFilter(53, SRC); - if (!device->setFilter(dnsResponseFilter)) - { - PCPP_LOG_ERROR("Couldn't set DNS response filter"); - return result; - } - - // since packet capture is done on another thread, I use a conditional mutex with timeout to synchronize between the capture - // thread and the main thread. When the capture thread starts running the main thread is blocking on the conditional mutex. - // When the DNS response are captured the capture thread signals the main thread and the main thread stops capturing and continues - // to the next iteration. if a timeout passes and no DNS response is captured, the main thread stops capturing - - std::mutex mutex; - std::condition_variable cond; - - // this is the token that passes between the 2 threads - DNSReceivedData data = { - mutex, - cond, - hostname, - transactionID, - clock(), - IPv4Address::Zero, - 0, - 0 - }; - - - struct timeval now; - gettimeofday(&now,nullptr); - - // start capturing. The capture is done on another thread, hence "dnsResponseReceived" is running on that thread - device->startCapture(dnsResponseReceived, &data); - - // send the DNS request - device->sendPacket(&dnsRequest); - - // block on the conditional mutex until capture thread signals or until timeout expires - // cppcheck-suppress localMutex - std::unique_lock lock(mutex); - std::cv_status res = cond.wait_for(lock, std::chrono::seconds(dnsTimeout)); - - // stop the capturing thread - device->stopCapture(); - - // check if timeout expired - if (res == std::cv_status::timeout) - { - PCPP_LOG_ERROR("DNS request time out"); - return result; - } - - if (closeDeviceAtTheEnd) - device->close(); - else - device->clearFilter(); - - result = data.result; - dnsResponseTimeMS = data.dnsResponseTime; - dnsTTL = data.ttl; - - return result; -} - -} // namespace pcpp +#define LOG_MODULE NetworkUtils + +#include +#include +#include +#include +#include "Logger.h" +#include "Packet.h" +#include "EthLayer.h" +#include "ArpLayer.h" +#include "IPv4Layer.h" +#include "UdpLayer.h" +#include "DnsLayer.h" +#include "PcapFilter.h" +#include "NetworkUtils.h" +#include "EndianPortable.h" +#ifdef _MSC_VER +#include "SystemUtils.h" +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT 10060 +#endif + +#define DNS_PORT 53 + + +namespace pcpp +{ + +const int NetworkUtils::DefaultTimeout = 5; + + +struct ArpingReceivedData +{ + std::mutex &mutex; + std::condition_variable &cond; + IPv4Address ipAddr; + clock_t start; + MacAddress result; + double arpResponseTime; +}; + + +static void arpPacketReceived(RawPacket* rawPacket, PcapLiveDevice* device, void* userCookie) +{ + // extract timestamp of packet + clock_t receiveTime = clock(); + + // get the data from the main thread + ArpingReceivedData* data = (ArpingReceivedData*)userCookie; + + // parse the response packet + Packet packet(rawPacket); + + // verify that it's an ARP packet (although it must be because I set an ARP reply filter on the interface) + if (!packet.isPacketOfType(ARP)) + return; + + // extract the ARP layer from the packet + ArpLayer* arpReplyLayer = packet.getLayerOfType(true); // lookup in reverse order + if (arpReplyLayer == nullptr) + return; + + // verify it's the right ARP response + if (arpReplyLayer->getArpHeader()->hardwareType != htobe16(1) /* Ethernet */ + || arpReplyLayer->getArpHeader()->protocolType != htobe16(PCPP_ETHERTYPE_IP)) + return; + + // verify the ARP response is the response for out request (and not some arbitrary ARP response) + if (arpReplyLayer->getSenderIpAddr() != data->ipAddr) + return; + + // measure response time + double diffticks = receiveTime-data->start; + double diffms = (diffticks*1000)/CLOCKS_PER_SEC; + + data->arpResponseTime = diffms; + data->result = arpReplyLayer->getSenderMacAddress(); + + // signal the main thread the ARP reply was received + data->cond.notify_one(); +} + + +MacAddress NetworkUtils::getMacAddress(IPv4Address ipAddr, PcapLiveDevice* device, double& arpResponseTimeMS, + MacAddress sourceMac, IPv4Address sourceIP, int arpTimeout) const +{ + MacAddress result = MacAddress::Zero; + + // open the device if not already opened + bool closeDeviceAtTheEnd = false; + if (!device->isOpened()) + { + closeDeviceAtTheEnd = true; + if (!device->open()) + { + PCPP_LOG_ERROR("Cannot open device"); + return result; + } + } + + if (sourceMac == MacAddress::Zero) + sourceMac = device->getMacAddress(); + + if (sourceIP == IPv4Address::Zero) + sourceIP = device->getIPv4Address(); + + if (arpTimeout <= 0) + arpTimeout = NetworkUtils::DefaultTimeout; + + // create an ARP request from sourceMac and sourceIP and ask for target IP + + Packet arpRequest(100); + + MacAddress destMac(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); + EthLayer ethLayer(sourceMac, destMac); + + ArpLayer arpLayer(ARP_REQUEST, sourceMac, destMac, sourceIP, ipAddr); + + if (!arpRequest.addLayer(ðLayer)) + { + PCPP_LOG_ERROR("Couldn't build Eth layer for ARP request"); + return result; + } + + if (!arpRequest.addLayer(&arpLayer)) + { + PCPP_LOG_ERROR("Couldn't build ARP layer for ARP request"); + return result; + } + + arpRequest.computeCalculateFields(); + + // set a filter for the interface to intercept only ARP response packets + ArpFilter arpFilter(ARP_REPLY); + if (!device->setFilter(arpFilter)) + { + PCPP_LOG_ERROR("Couldn't set ARP filter for device"); + return result; + } + + // since packet capture is done on another thread, I use a conditional mutex with timeout to synchronize between the capture + // thread and the main thread. When the capture thread starts running the main thread is blocking on the conditional mutex. + // When the ARP response is captured the capture thread signals the main thread and the main thread stops capturing and continues + // to the next iteration. If a timeout passes and no ARP response is captured, the main thread stops capturing + + std::mutex mutex; + std::condition_variable cond; + + // this is the token that passes between the 2 threads. It contains pointers to the conditional mutex, the target IP for identifying + // the ARP response, the iteration index and a timestamp to calculate the response time + ArpingReceivedData data = { + mutex, + cond, + ipAddr, + clock(), + MacAddress::Zero, + 0 + }; + + struct timeval now; + gettimeofday(&now,nullptr); + + // start capturing. The capture is done on another thread, hence "arpPacketReceived" is running on that thread + device->startCapture(arpPacketReceived, &data); + + // send the ARP request + device->sendPacket(&arpRequest); + + // block on the conditional mutex until capture thread signals or until timeout expires + // cppcheck-suppress localMutex + std::unique_lock lock(mutex); + std::cv_status res = cond.wait_for(lock, std::chrono::seconds(arpTimeout)); + + // stop the capturing thread + device->stopCapture(); + + // check if timeout expired + if (res == std::cv_status::timeout) + { + PCPP_LOG_ERROR("ARP request time out"); + return result; + } + + if (closeDeviceAtTheEnd) + device->close(); + else + device->clearFilter(); + + result = data.result; + arpResponseTimeMS = data.arpResponseTime; + + return result; +} + + + +struct DNSReceivedData +{ + std::mutex &mutex; + std::condition_variable &cond; + std::string hostname; + uint16_t transactionID; + clock_t start; + IPv4Address result; + uint32_t ttl; + double dnsResponseTime; +}; + +static void dnsResponseReceived(RawPacket* rawPacket, PcapLiveDevice* device, void* userCookie) +{ + // extract timestamp of packet + clock_t receiveTime = clock(); + + // get data from the main thread + DNSReceivedData* data = (DNSReceivedData*)userCookie; + + // parse the response packet + Packet packet(rawPacket); + + // verify that it's an DNS packet (although it must be because DNS port filter was set on the interface) + if (!packet.isPacketOfType(DNS)) + return; + + // extract the DNS layer from the packet + DnsLayer* dnsResponseLayer = packet.getLayerOfType(true); // lookup in reverse order + if (dnsResponseLayer == nullptr) + return; + + // verify it's the right DNS response + if (dnsResponseLayer->getDnsHeader()->queryOrResponse != 1 /* DNS response */ + || dnsResponseLayer->getDnsHeader()->numberOfAnswers < htobe16(1) + || dnsResponseLayer->getDnsHeader()->transactionID != htobe16(data->transactionID)) + { + return; + } + + // DNS resolving can be recursive as many DNS responses contain multiple answers with recursive canonical names (CNAME) for + // the hostname. For example: a DNS response for www.a.com can have multiple answers: + //- First with CNAME: www.a.com -> www.b.com + //- Second with CNAME: www.b.com -> www.c.com + //- Third with resolving: www.c.com -> 1.1.1.1 + // So the search must be recursive until an IPv4 resolving is found or until no hostname or canonical name are found (and then return) + + std::string hostToFind = data->hostname; + + DnsResource* dnsAnswer = nullptr; + + while (true) + { + dnsAnswer = dnsResponseLayer->getAnswer(hostToFind, true); + + // if response doesn't contain hostname or cname - return + if (dnsAnswer == nullptr) + { + PCPP_LOG_DEBUG("DNS answer doesn't contain hostname '" << hostToFind << "'"); + return; + } + + DnsType dnsType = dnsAnswer->getDnsType(); + // if answer contains IPv4 resolving - break the loop and return the IP address + if (dnsType == DNS_TYPE_A) + { + PCPP_LOG_DEBUG("Found IPv4 resolving for hostname '" << hostToFind << "'"); + break; + } + // if answer contains a cname - continue to search this cname in the packet - hopefully find the IP resolving + else if (dnsType == DNS_TYPE_CNAME) + { + PCPP_LOG_DEBUG("Got a DNS response for hostname '" << hostToFind << "' with CNAME '" << dnsAnswer->getData()->toString() << "'"); + hostToFind = dnsAnswer->getData()->toString(); + } + // if answer is of type other than A or CNAME (for example AAAA - IPv6) - type is not supported - return + else + { + PCPP_LOG_DEBUG("Got a DNS response with type which is not A or CNAME"); + return; + } + } + // if we got here it means an IPv4 resolving was found + + // measure response time + clock_t diffticks = receiveTime-data->start; + double diffms = (diffticks*1000.0)/CLOCKS_PER_SEC; + + data->dnsResponseTime = diffms; + data->result = dnsAnswer->getData()->castAs()->getIpAddress(); + data->ttl = dnsAnswer->getTTL(); + + // signal the main thread the ARP reply was received + data->cond.notify_one(); +} + + +IPv4Address NetworkUtils::getIPv4Address(const std::string& hostname, PcapLiveDevice* device, double& dnsResponseTimeMS, uint32_t& dnsTTL, + int dnsTimeout, IPv4Address dnsServerIP, IPv4Address gatewayIP) const +{ + IPv4Address result = IPv4Address::Zero; + + // open the device if not already opened + bool closeDeviceAtTheEnd = false; + if (!device->isOpened()) + { + closeDeviceAtTheEnd = true; + if (!device->open()) + { + PCPP_LOG_ERROR("Cannot open device"); + return result; + } + } + + // first - resolve gateway MAC address + + // if gateway IP wasn't provided - try to find the default gateway + if (gatewayIP == IPv4Address::Zero) + { + gatewayIP = device->getDefaultGateway(); + } + + if (!gatewayIP.isValid() || gatewayIP == IPv4Address::Zero) + { + PCPP_LOG_ERROR("Gateway address isn't valid or couldn't find default gateway"); + return result; + } + + // send the ARP request to find gateway MAC address + double arpResTime; + MacAddress gatewayMacAddress = getMacAddress(gatewayIP, device, arpResTime); + + if (gatewayMacAddress == MacAddress::Zero) + { + PCPP_LOG_ERROR("Couldn't resolve gateway MAC address"); + return result; + } + + if (dnsTimeout <= 0) + dnsTimeout = NetworkUtils::DefaultTimeout; + + // validate DNS server IP. If it wasn't provided - set the system-configured DNS server + if (dnsServerIP == IPv4Address::Zero && device->getDnsServers().size() > 0) + { + dnsServerIP = device->getDnsServers().at(0); + } + + if (!dnsServerIP.isValid()) + { + PCPP_LOG_ERROR("DNS server IP isn't valid"); + return result; + } + + // create DNS request + + Packet dnsRequest(100); + MacAddress sourceMac = device->getMacAddress(); + EthLayer ethLayer(sourceMac, gatewayMacAddress, PCPP_ETHERTYPE_IP); + IPv4Layer ipLayer(device->getIPv4Address(), dnsServerIP); + ipLayer.getIPv4Header()->timeToLive = 128; + + // randomize source port to a number >= 10000 + int srcPortLowest = 10000; + int srcPortRange = 65535 - srcPortLowest; + uint16_t srcPort = (rand() % srcPortRange) + srcPortLowest; + UdpLayer udpLayer(srcPort, DNS_PORT); + + // create the DNS request for the hostname + DnsLayer dnsLayer; + + // randomize transaction ID + uint16_t transactionID = rand() % 65535; + dnsLayer.getDnsHeader()->transactionID = htobe16(transactionID); + dnsLayer.addQuery(hostname, DNS_TYPE_A, DNS_CLASS_IN); + + // add all layers to packet + if (!dnsRequest.addLayer(ðLayer) || !dnsRequest.addLayer(&ipLayer) || !dnsRequest.addLayer(&udpLayer) || !dnsRequest.addLayer(&dnsLayer)) + { + PCPP_LOG_ERROR("Couldn't construct DNS query"); + return result; + } + + dnsRequest.computeCalculateFields(); + + // set a DNS response filter on the device + PortFilter dnsResponseFilter(53, SRC); + if (!device->setFilter(dnsResponseFilter)) + { + PCPP_LOG_ERROR("Couldn't set DNS response filter"); + return result; + } + + // since packet capture is done on another thread, I use a conditional mutex with timeout to synchronize between the capture + // thread and the main thread. When the capture thread starts running the main thread is blocking on the conditional mutex. + // When the DNS response are captured the capture thread signals the main thread and the main thread stops capturing and continues + // to the next iteration. if a timeout passes and no DNS response is captured, the main thread stops capturing + + std::mutex mutex; + std::condition_variable cond; + + // this is the token that passes between the 2 threads + DNSReceivedData data = { + mutex, + cond, + hostname, + transactionID, + clock(), + IPv4Address::Zero, + 0, + 0 + }; + + + struct timeval now; + gettimeofday(&now,nullptr); + + // start capturing. The capture is done on another thread, hence "dnsResponseReceived" is running on that thread + device->startCapture(dnsResponseReceived, &data); + + // send the DNS request + device->sendPacket(&dnsRequest); + + // block on the conditional mutex until capture thread signals or until timeout expires + // cppcheck-suppress localMutex + std::unique_lock lock(mutex); + std::cv_status res = cond.wait_for(lock, std::chrono::seconds(dnsTimeout)); + + // stop the capturing thread + device->stopCapture(); + + // check if timeout expired + if (res == std::cv_status::timeout) + { + PCPP_LOG_ERROR("DNS request time out"); + return result; + } + + if (closeDeviceAtTheEnd) + device->close(); + else + device->clearFilter(); + + result = data.result; + dnsResponseTimeMS = data.dnsResponseTime; + dnsTTL = data.ttl; + + return result; +} + +} // namespace pcpp diff --git a/Pcap++/src/PcapDevice.cpp b/Pcap++/src/PcapDevice.cpp index 6242a1b5c2..a8bc77267e 100644 --- a/Pcap++/src/PcapDevice.cpp +++ b/Pcap++/src/PcapDevice.cpp @@ -1,70 +1,70 @@ -#include "PcapDevice.h" -#include "PcapFilter.h" -#include "Logger.h" -#include "TimespecTimeval.h" -#include "pcap.h" - -namespace pcpp -{ - -IPcapDevice::~IPcapDevice() -{ -} - -bool IPcapDevice::setFilter(std::string filterAsString) -{ - PCPP_LOG_DEBUG("Filter to be set: '" << filterAsString << "'"); - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device not Opened!! cannot set filter"); - return false; - } - - struct bpf_program prog; - PCPP_LOG_DEBUG("Compiling the filter '" << filterAsString << "'"); - if (pcap_compile(m_PcapDescriptor, &prog, filterAsString.c_str(), 1, 0) < 0) - { - /* - * Print out appropriate text, followed by the error message - * generated by the packet capture library. - */ - PCPP_LOG_ERROR("Error compiling filter. Error message is: " << pcap_geterr(m_PcapDescriptor)); - return false; - } - - PCPP_LOG_DEBUG("Setting the compiled filter"); - if (pcap_setfilter(m_PcapDescriptor, &prog) < 0) - { - /* - * Print out error. The format will be the prefix string, - * created above, followed by the error message that the packet - * capture library generates. - */ - PCPP_LOG_ERROR("Error setting a compiled filter. Error message is: " << pcap_geterr(m_PcapDescriptor)); - pcap_freecode(&prog); - return false; - } - - PCPP_LOG_DEBUG("Filter set successfully"); - - pcap_freecode(&prog); - - return true; -} - -bool IPcapDevice::clearFilter() -{ - return setFilter(""); -} - -bool IPcapDevice::matchPacketWithFilter(GeneralFilter& filter, RawPacket* rawPacket) -{ - return filter.matchPacketWithFilter(rawPacket); -} - -std::string IPcapDevice::getPcapLibVersionInfo() -{ - return std::string(pcap_lib_version()); -} - -} // namespace pcpp +#include "PcapDevice.h" +#include "PcapFilter.h" +#include "Logger.h" +#include "TimespecTimeval.h" +#include "pcap.h" + +namespace pcpp +{ + +IPcapDevice::~IPcapDevice() +{ +} + +bool IPcapDevice::setFilter(std::string filterAsString) +{ + PCPP_LOG_DEBUG("Filter to be set: '" << filterAsString << "'"); + if (!m_DeviceOpened) + { + PCPP_LOG_ERROR("Device not Opened!! cannot set filter"); + return false; + } + + struct bpf_program prog; + PCPP_LOG_DEBUG("Compiling the filter '" << filterAsString << "'"); + if (pcap_compile(m_PcapDescriptor, &prog, filterAsString.c_str(), 1, 0) < 0) + { + /* + * Print out appropriate text, followed by the error message + * generated by the packet capture library. + */ + PCPP_LOG_ERROR("Error compiling filter. Error message is: " << pcap_geterr(m_PcapDescriptor)); + return false; + } + + PCPP_LOG_DEBUG("Setting the compiled filter"); + if (pcap_setfilter(m_PcapDescriptor, &prog) < 0) + { + /* + * Print out error. The format will be the prefix string, + * created above, followed by the error message that the packet + * capture library generates. + */ + PCPP_LOG_ERROR("Error setting a compiled filter. Error message is: " << pcap_geterr(m_PcapDescriptor)); + pcap_freecode(&prog); + return false; + } + + PCPP_LOG_DEBUG("Filter set successfully"); + + pcap_freecode(&prog); + + return true; +} + +bool IPcapDevice::clearFilter() +{ + return setFilter(""); +} + +bool IPcapDevice::matchPacketWithFilter(GeneralFilter& filter, RawPacket* rawPacket) +{ + return filter.matchPacketWithFilter(rawPacket); +} + +std::string IPcapDevice::getPcapLibVersionInfo() +{ + return std::string(pcap_lib_version()); +} + +} // namespace pcpp diff --git a/Pcap++/src/PcapFileDevice.cpp b/Pcap++/src/PcapFileDevice.cpp index 96053c2dff..f894ee8292 100644 --- a/Pcap++/src/PcapFileDevice.cpp +++ b/Pcap++/src/PcapFileDevice.cpp @@ -1,919 +1,919 @@ -#define LOG_MODULE PcapLogModuleFileDevice - -#include -#include -#include "PcapFileDevice.h" -#include "light_pcapng_ext.h" -#include "Logger.h" -#include "TimespecTimeval.h" -#include "pcap.h" -#include -#include -#include "EndianPortable.h" - -namespace pcpp -{ - -template -constexpr size_t ARRAY_SIZE(T (&)[N]) { return N; } - -struct pcap_file_header -{ - uint32_t magic; - uint16_t version_major; - uint16_t version_minor; - int32_t thiszone; - uint32_t sigfigs; - uint32_t snaplen; - uint32_t linktype; -}; - -struct packet_header -{ - uint32_t tv_sec; - uint32_t tv_usec; - uint32_t caplen; - uint32_t len; -}; - -// ~~~~~~~~~~~~~~~~~~~ -// IFileDevice members -// ~~~~~~~~~~~~~~~~~~~ - -IFileDevice::IFileDevice(const std::string& fileName) : IPcapDevice() -{ - m_FileName = fileName; -} - -IFileDevice::~IFileDevice() -{ - IFileDevice::close(); -} - -std::string IFileDevice::getFileName() const -{ - return m_FileName; -} - -void IFileDevice::close() -{ - if (m_PcapDescriptor != nullptr) - { - pcap_close(m_PcapDescriptor); - PCPP_LOG_DEBUG("Successfully closed file reader device for filename '" << m_FileName << "'"); - m_PcapDescriptor = nullptr; - } - - m_DeviceOpened = false; -} - - -// ~~~~~~~~~~~~~~~~~~~~~~~~~ -// IFileReaderDevice members -// ~~~~~~~~~~~~~~~~~~~~~~~~~ - -IFileReaderDevice::IFileReaderDevice(const std::string& fileName) : IFileDevice(fileName) -{ - m_NumOfPacketsNotParsed = 0; - m_NumOfPacketsRead = 0; -} - -IFileReaderDevice* IFileReaderDevice::getReader(const std::string& fileName) -{ - const auto extensionPos = fileName.find_last_of('.'); - const auto fileExtension = extensionPos != std::string::npos ? fileName.substr(extensionPos) : ""; - - if (fileExtension == ".pcapng" || fileExtension == ".zstd" || fileExtension == ".zst") - return new PcapNgFileReaderDevice(fileName); - else if (fileExtension == ".snoop") - return new SnoopFileReaderDevice(fileName); - - return new PcapFileReaderDevice(fileName); -} - -uint64_t IFileReaderDevice::getFileSize() const -{ - std::ifstream fileStream(m_FileName.c_str(), std::ifstream::ate | std::ifstream::binary); - return fileStream.tellg(); -} - -int IFileReaderDevice::getNextPackets(RawPacketVector& packetVec, int numOfPacketsToRead) -{ - int numOfPacketsRead = 0; - - for (; numOfPacketsToRead < 0 || numOfPacketsRead < numOfPacketsToRead; numOfPacketsRead++) - { - RawPacket* newPacket = new RawPacket(); - bool packetRead = getNextPacket(*newPacket); - if (packetRead) - { - packetVec.pushBack(newPacket); - } - else - { - delete newPacket; - break; - } - } - - return numOfPacketsRead; -} - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// SnoopFileReaderDevice members -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -SnoopFileReaderDevice::~SnoopFileReaderDevice() -{ - m_snoopFile.close(); -} - -bool SnoopFileReaderDevice::open() -{ - m_NumOfPacketsRead = 0; - m_NumOfPacketsNotParsed = 0; - - m_snoopFile.open(m_FileName.c_str(), std::ifstream::binary); - if (!m_snoopFile.is_open()) - { - PCPP_LOG_ERROR("Cannot open snoop reader device for filename '" << m_FileName << "'"); - m_snoopFile.close(); - return false; - } - - snoop_file_header_t snoop_file_header; - m_snoopFile.read((char*)&snoop_file_header, sizeof(snoop_file_header_t)); - if (!m_snoopFile) - { - PCPP_LOG_ERROR("Cannot read snoop file header for '" << m_FileName << "'"); - m_snoopFile.close(); - return false; - } - - if(be64toh(snoop_file_header.identification_pattern) != 0x736e6f6f70000000 && be32toh(snoop_file_header.version_number) == 2) - return false; - - // From https://datatracker.ietf.org/doc/html/rfc1761 - static const pcpp::LinkLayerType snoop_encap[] = { - LINKTYPE_ETHERNET, /* IEEE 802.3 */ - LINKTYPE_NULL, /* IEEE 802.4 Token Bus */ - LINKTYPE_IEEE802_5, /* IEEE 802.5 */ - LINKTYPE_NULL, /* IEEE 802.6 Metro Net */ - LINKTYPE_ETHERNET, /* Ethernet */ - LINKTYPE_C_HDLC, /* HDLC */ - LINKTYPE_NULL, /* Character Synchronous, e.g. bisync */ - LINKTYPE_NULL, /* IBM Channel-to-Channel */ - LINKTYPE_FDDI /* FDDI */ - }; - uint32_t datalink_type = be32toh(snoop_file_header.datalink_type); - if (datalink_type > ARRAY_SIZE(snoop_encap) - 1) - { - PCPP_LOG_ERROR("Cannot read data link type for '" << m_FileName << "'"); - m_snoopFile.close(); - return false; - } - - m_PcapLinkLayerType = snoop_encap[datalink_type]; - - PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" << m_FileName << "'"); - m_DeviceOpened = true; - return true; -} - -void SnoopFileReaderDevice::getStatistics(PcapStats& stats) const -{ - stats.packetsRecv = m_NumOfPacketsRead; - stats.packetsDrop = m_NumOfPacketsNotParsed; - stats.packetsDropByInterface = 0; - PCPP_LOG_DEBUG("Statistics received for reader device for filename '" << m_FileName << "'"); -} - -bool SnoopFileReaderDevice::getNextPacket(RawPacket& rawPacket) -{ - rawPacket.clear(); - if (m_DeviceOpened != true) - { - PCPP_LOG_ERROR("File device '" << m_FileName << "' not opened"); - return false; - } - snoop_packet_header_t snoop_packet_header; - m_snoopFile.read((char*)&snoop_packet_header, sizeof(snoop_packet_header_t)); - if(!m_snoopFile) { - return false; - } - size_t packetSize = be32toh(snoop_packet_header.included_length); - if(packetSize > 15000) { - return false; - } - char* packetData = new char[packetSize]; - m_snoopFile.read(packetData, packetSize); - if(!m_snoopFile) { - return false; - } - timespec ts = { static_cast(be32toh(snoop_packet_header.time_sec)), static_cast(be32toh(snoop_packet_header.time_usec)) * 1000 }; - if (!rawPacket.setRawData((const uint8_t*)packetData, packetSize, ts, static_cast(m_PcapLinkLayerType))) - { - PCPP_LOG_ERROR("Couldn't set data to raw packet"); - return false; - } - size_t pad = be32toh(snoop_packet_header.packet_record_length) - (sizeof(snoop_packet_header_t) + be32toh(snoop_packet_header.included_length)); - m_snoopFile.ignore(pad); - if(!m_snoopFile) { - return false; - } - - m_NumOfPacketsRead++; - return true; -} - -void SnoopFileReaderDevice::close() -{ - m_snoopFile.close(); - m_DeviceOpened = false; - PCPP_LOG_DEBUG("File reader closed for file '" << m_FileName << "'"); -} - - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// PcapFileReaderDevice members -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -bool PcapFileReaderDevice::open() -{ - m_NumOfPacketsRead = 0; - m_NumOfPacketsNotParsed = 0; - - if (m_PcapDescriptor != nullptr) - { - PCPP_LOG_DEBUG("Pcap descriptor already opened. Nothing to do"); - return true; - } - - char errbuf[PCAP_ERRBUF_SIZE]; -#if defined(PCAP_TSTAMP_PRECISION_NANO) - m_PcapDescriptor = pcap_open_offline_with_tstamp_precision(m_FileName.c_str(), PCAP_TSTAMP_PRECISION_NANO, errbuf); -#else - m_PcapDescriptor = pcap_open_offline(m_FileName.c_str(), errbuf); -#endif - if (m_PcapDescriptor == nullptr) - { - PCPP_LOG_ERROR("Cannot open file reader device for filename '" << m_FileName << "': " << errbuf); - m_DeviceOpened = false; - return false; - } - - int linkLayer = pcap_datalink(m_PcapDescriptor); - if (!RawPacket::isLinkTypeValid(linkLayer)) - { - PCPP_LOG_ERROR("Invalid link layer (" << linkLayer << ") for reader device filename '" << m_FileName << "'"); - pcap_close(m_PcapDescriptor); - m_PcapDescriptor = nullptr; - m_DeviceOpened = false; - return false; - } - - m_PcapLinkLayerType = static_cast(linkLayer); - - PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" << m_FileName << "'"); - m_DeviceOpened = true; - return true; -} - -void PcapFileReaderDevice::getStatistics(PcapStats& stats) const -{ - stats.packetsRecv = m_NumOfPacketsRead; - stats.packetsDrop = m_NumOfPacketsNotParsed; - stats.packetsDropByInterface = 0; - PCPP_LOG_DEBUG("Statistics received for reader device for filename '" << m_FileName << "'"); -} - -bool PcapFileReaderDevice::getNextPacket(RawPacket& rawPacket) -{ - rawPacket.clear(); - if (m_PcapDescriptor == nullptr) - { - PCPP_LOG_ERROR("File device '" << m_FileName << "' not opened"); - return false; - } - pcap_pkthdr pkthdr; - const uint8_t* pPacketData = pcap_next(m_PcapDescriptor, &pkthdr); - if (pPacketData == nullptr) - { - PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file"); - return false; - } - - uint8_t* pMyPacketData = new uint8_t[pkthdr.caplen]; - memcpy(pMyPacketData, pPacketData, pkthdr.caplen); -#if defined(PCAP_TSTAMP_PRECISION_NANO) - timespec ts = { pkthdr.ts.tv_sec, static_cast(pkthdr.ts.tv_usec) }; //because we opened with nano second precision 'tv_usec' is actually nanos -#else - struct timeval ts = pkthdr.ts; -#endif - if (!rawPacket.setRawData(pMyPacketData, pkthdr.caplen, ts, static_cast(m_PcapLinkLayerType), pkthdr.len)) - { - PCPP_LOG_ERROR("Couldn't set data to raw packet"); - return false; - } - m_NumOfPacketsRead++; - return true; -} - - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// PcapNgFileReaderDevice members -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -PcapNgFileReaderDevice::PcapNgFileReaderDevice(const std::string& fileName) : IFileReaderDevice(fileName) -{ - m_LightPcapNg = nullptr; -} - -bool PcapNgFileReaderDevice::open() -{ - m_NumOfPacketsRead = 0; - m_NumOfPacketsNotParsed = 0; - - if (m_LightPcapNg != nullptr) - { - PCPP_LOG_DEBUG("pcapng descriptor already opened. Nothing to do"); - return true; - } - - m_LightPcapNg = light_pcapng_open_read(m_FileName.c_str(), LIGHT_FALSE); - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Cannot open pcapng reader device for filename '" << m_FileName << "'"); - m_DeviceOpened = false; - return false; - } - - PCPP_LOG_DEBUG("Successfully opened pcapng reader device for filename '" << m_FileName << "'"); - m_DeviceOpened = true; - return true; -} - -bool PcapNgFileReaderDevice::getNextPacket(RawPacket& rawPacket, std::string& packetComment) -{ - rawPacket.clear(); - packetComment = ""; - - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); - return false; - } - - light_packet_header pktHeader; - const uint8_t* pktData = nullptr; - - if (!light_get_next_packet((light_pcapng_t*)m_LightPcapNg, &pktHeader, &pktData)) - { - PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file"); - return false; - } - - while (!m_BpfWrapper.matchPacketWithFilter(pktData, pktHeader.captured_length, pktHeader.timestamp, pktHeader.data_link)) - { - if (!light_get_next_packet((light_pcapng_t*)m_LightPcapNg, &pktHeader, &pktData)) - { - PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file"); - return false; - } - } - - uint8_t* myPacketData = new uint8_t[pktHeader.captured_length]; - memcpy(myPacketData, pktData, pktHeader.captured_length); - if (!rawPacket.setRawData(myPacketData, pktHeader.captured_length, pktHeader.timestamp, static_cast(pktHeader.data_link), pktHeader.original_length)) - { - PCPP_LOG_ERROR("Couldn't set data to raw packet"); - return false; - } - - if (pktHeader.comment != nullptr && pktHeader.comment_length > 0) - packetComment = std::string(pktHeader.comment, pktHeader.comment_length); - - m_NumOfPacketsRead++; - return true; -} - -bool PcapNgFileReaderDevice::getNextPacket(RawPacket& rawPacket) -{ - std::string temp; - return getNextPacket(rawPacket, temp); -} - -void PcapNgFileReaderDevice::getStatistics(PcapStats& stats) const -{ - stats.packetsRecv = m_NumOfPacketsRead; - stats.packetsDrop = m_NumOfPacketsNotParsed; - stats.packetsDropByInterface = 0; - PCPP_LOG_DEBUG("Statistics received for pcapng reader device for filename '" << m_FileName << "'"); -} - -bool PcapNgFileReaderDevice::setFilter(std::string filterAsString) -{ - return m_BpfWrapper.setFilter(filterAsString); -} - -void PcapNgFileReaderDevice::close() -{ - if (m_LightPcapNg == nullptr) - return; - - light_pcapng_close((light_pcapng_t*)m_LightPcapNg); - m_LightPcapNg = nullptr; - - m_DeviceOpened = false; - PCPP_LOG_DEBUG("File reader closed for file '" << m_FileName << "'"); -} - - -std::string PcapNgFileReaderDevice::getOS() const -{ - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); - return ""; - } - - light_pcapng_file_info* fileInfo = light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); - char* res = fileInfo->os_desc; - size_t len = fileInfo->os_desc_size; - if (len == 0 || res == nullptr) - return ""; - - return std::string(res, len); -} - -std::string PcapNgFileReaderDevice::getHardware() const -{ - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); - return ""; - } - - light_pcapng_file_info* fileInfo = light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); - char* res = fileInfo->hardware_desc; - size_t len = fileInfo->hardware_desc_size; - if (len == 0 || res == nullptr) - return ""; - - return std::string(res, len); -} - -std::string PcapNgFileReaderDevice::getCaptureApplication() const -{ - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); - return ""; - } - - light_pcapng_file_info* fileInfo = light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); - char* res = fileInfo->user_app_desc; - size_t len = fileInfo->user_app_desc_size; - if (len == 0 || res == nullptr) - return ""; - - return std::string(res, len); -} - -std::string PcapNgFileReaderDevice::getCaptureFileComment() const -{ - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); - return ""; - } - - light_pcapng_file_info* fileInfo = light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); - char* res = fileInfo->file_comment; - size_t len = fileInfo->file_comment_size; - if (len == 0 || res == nullptr) - return ""; - - return std::string(res, len); -} - - -// ~~~~~~~~~~~~~~~~~~~~~~~~~ -// IFileWriterDevice members -// ~~~~~~~~~~~~~~~~~~~~~~~~~ - -IFileWriterDevice:: IFileWriterDevice(const std::string& fileName) : IFileDevice(fileName) -{ - m_NumOfPacketsNotWritten = 0; - m_NumOfPacketsWritten = 0; -} - - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// PcapFileWriterDevice members -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -PcapFileWriterDevice::PcapFileWriterDevice(const std::string& fileName, LinkLayerType linkLayerType) : IFileWriterDevice(fileName) -{ - m_PcapDumpHandler = nullptr; - m_NumOfPacketsNotWritten = 0; - m_NumOfPacketsWritten = 0; - m_PcapLinkLayerType = linkLayerType; - m_AppendMode = false; - m_File = nullptr; -} - -void PcapFileWriterDevice::closeFile() -{ - if (m_AppendMode && m_File != nullptr) - { - fclose(m_File); - m_File = nullptr; - } -} - -bool PcapFileWriterDevice::writePacket(RawPacket const& packet) -{ - if ((!m_AppendMode && m_PcapDescriptor == nullptr) || (m_PcapDumpHandler == nullptr)) - { - PCPP_LOG_ERROR("Device not opened"); - m_NumOfPacketsNotWritten++; - return false; - } - - if (packet.getLinkLayerType() != m_PcapLinkLayerType) - { - PCPP_LOG_ERROR("Cannot write a packet with a different link layer type"); - m_NumOfPacketsNotWritten++; - return false; - } - - pcap_pkthdr pktHdr; - pktHdr.caplen = ((RawPacket&)packet).getRawDataLen(); - pktHdr.len = ((RawPacket&)packet).getFrameLength(); - timespec packet_timestamp = ((RawPacket&)packet).getPacketTimeStamp(); - TIMESPEC_TO_TIMEVAL(&pktHdr.ts, &packet_timestamp); - if (!m_AppendMode) - pcap_dump((uint8_t*)m_PcapDumpHandler, &pktHdr, ((RawPacket&)packet).getRawData()); - else - { - // Below are actually the lines run by pcap_dump. The reason I had to put them instead pcap_dump is that on Windows using WinPcap/Npcap - // you can't pass pointers between libraries compiled with different compilers. In this case - PcapPlusPlus and WinPcap/Npcap weren't - // compiled with the same compiler so it's impossible to fopen a file in PcapPlusPlus, pass the pointer to WinPcap/Npcap and use the - // FILE* pointer there. Doing this throws an exception. So the only option when implementing append to pcap is to write all relevant - // WinPcap/Npcap code that handles opening/closing/writing to pcap files inside PcapPlusPlus code - - // the reason to create this packet_header struct is timeval has different sizes in 32-bit and 64-bit systems, - // but pcap format uses the 32-bit timeval version, so we need to align timeval to that - packet_header pktHdrTemp; - pktHdrTemp.tv_sec = pktHdr.ts.tv_sec; - pktHdrTemp.tv_usec = pktHdr.ts.tv_usec; - pktHdrTemp.caplen = pktHdr.caplen; - pktHdrTemp.len = pktHdr.len; - fwrite(&pktHdrTemp, sizeof(pktHdrTemp), 1, m_File); - fwrite(((RawPacket&)packet).getRawData(), pktHdrTemp.caplen, 1, m_File); - } - PCPP_LOG_DEBUG("Packet written successfully to '" << m_FileName << "'"); - m_NumOfPacketsWritten++; - return true; -} - -bool PcapFileWriterDevice::writePackets(const RawPacketVector& packets) -{ - for (RawPacketVector::ConstVectorIterator iter = packets.begin(); iter != packets.end(); iter++) - { - if (!writePacket(**iter)) - return false; - } - - return true; -} - -bool PcapFileWriterDevice::open() -{ - if (m_PcapDescriptor != nullptr) - { - PCPP_LOG_DEBUG("Pcap descriptor already opened. Nothing to do"); - return true; - } - - switch(m_PcapLinkLayerType) - { - case LINKTYPE_RAW: - case LINKTYPE_DLT_RAW2: - PCPP_LOG_ERROR("The only Raw IP link type supported in libpcap/WinPcap/Npcap is LINKTYPE_DLT_RAW1, please use that instead"); - return false; - default: - break; - } - - m_NumOfPacketsNotWritten = 0; - m_NumOfPacketsWritten = 0; - - m_PcapDescriptor = pcap_open_dead(m_PcapLinkLayerType, PCPP_MAX_PACKET_SIZE); - if (m_PcapDescriptor == nullptr) - { - PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName << "': pcap_open_dead returned NULL"); - m_DeviceOpened = false; - return false; - } - - - m_PcapDumpHandler = pcap_dump_open(m_PcapDescriptor, m_FileName.c_str()); - if (m_PcapDumpHandler == nullptr) - { - PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName << "': pcap_dump_open returned NULL with error: '" << pcap_geterr(m_PcapDescriptor) << "'"); - m_DeviceOpened = false; - return false; - } - - m_DeviceOpened = true; - PCPP_LOG_DEBUG("File writer device for file '" << m_FileName << "' opened successfully"); - return true; -} - -void PcapFileWriterDevice::flush() -{ - if (!m_DeviceOpened) - return; - - if (!m_AppendMode && pcap_dump_flush(m_PcapDumpHandler) == -1) - { - PCPP_LOG_ERROR("Error while flushing the packets to file"); - } - // in append mode it's impossible to use pcap_dump_flush, see comment above pcap_dump - else if (m_AppendMode && fflush(m_File) == EOF) - { - PCPP_LOG_ERROR("Error while flushing the packets to file"); - } - -} - -void PcapFileWriterDevice::close() -{ - if (!m_DeviceOpened) - return; - - flush(); - - IFileDevice::close(); - - if (!m_AppendMode && m_PcapDumpHandler != nullptr) - { - pcap_dump_close(m_PcapDumpHandler); - } - else if (m_AppendMode && m_File != nullptr) - { - // in append mode it's impossible to use pcap_dump_close, see comment above pcap_dump - fclose(m_File); - } - - m_PcapDumpHandler = nullptr; - m_File = nullptr; - PCPP_LOG_DEBUG("File writer closed for file '" << m_FileName << "'"); -} - -void PcapFileWriterDevice::getStatistics(PcapStats& stats) const -{ - stats.packetsRecv = m_NumOfPacketsWritten; - stats.packetsDrop = m_NumOfPacketsNotWritten; - stats.packetsDropByInterface = 0; - PCPP_LOG_DEBUG("Statistics received for writer device for filename '" << m_FileName << "'"); -} - -bool PcapFileWriterDevice::open(bool appendMode) -{ - if (!appendMode) - return open(); - - m_AppendMode = appendMode; - -#if !defined(_WIN32) - m_File = fopen(m_FileName.c_str(), "r+"); -#else - m_File = fopen(m_FileName.c_str(), "rb+"); -#endif - - if (m_File == nullptr) - { - PCPP_LOG_ERROR("Cannot open '" << m_FileName << "' for reading and writing"); - return false; - } - - pcap_file_header pcapFileHeader; - int amountRead = fread(&pcapFileHeader, 1, sizeof(pcapFileHeader), m_File); - if (amountRead != sizeof(pcap_file_header)) - { - if (ferror(m_File)) - PCPP_LOG_ERROR("Cannot read pcap header from file '" << m_FileName << "', error was: " << errno); - else - PCPP_LOG_ERROR("Cannot read pcap header from file '" << m_FileName << "', unknown error"); - - closeFile(); - return false; - } - - LinkLayerType linkLayerType = static_cast(pcapFileHeader.linktype); - if (linkLayerType != m_PcapLinkLayerType) - { - PCPP_LOG_ERROR("Pcap file has a different link layer type than the one chosen in PcapFileWriterDevice c'tor, " << linkLayerType << ", " << m_PcapLinkLayerType); - closeFile(); - return false; - } - - if (fseek(m_File, 0, SEEK_END) == -1) - { - PCPP_LOG_ERROR("Cannot read pcap file '" << m_FileName << "' to it's end, error was: " << errno); - closeFile(); - return false; - } - - m_PcapDumpHandler = ((pcap_dumper_t *)m_File); - - m_DeviceOpened = true; - PCPP_LOG_DEBUG("File writer device for file '" << m_FileName << "' opened successfully in append mode"); - return true; -} - - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// PcapNgFileWriterDevice members -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -PcapNgFileWriterDevice::PcapNgFileWriterDevice(const std::string& fileName, int compressionLevel) : IFileWriterDevice(fileName) -{ - m_LightPcapNg = nullptr; - m_CompressionLevel = compressionLevel; -} - -bool PcapNgFileWriterDevice::open(const std::string& os, const std::string& hardware, const std::string& captureApp, const std::string& fileComment) -{ - if (m_LightPcapNg != nullptr) - { - PCPP_LOG_DEBUG("Pcap-ng descriptor already opened. Nothing to do"); - return true; - } - - m_NumOfPacketsNotWritten = 0; - m_NumOfPacketsWritten = 0; - - light_pcapng_file_info* info = light_create_file_info(os.c_str(), hardware.c_str(), captureApp.c_str(), fileComment.c_str()); - - m_LightPcapNg = light_pcapng_open_write(m_FileName.c_str(), info, m_CompressionLevel); - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName << "': light_pcapng_open_write returned NULL"); - - light_free_file_info(info); - - m_DeviceOpened = false; - return false; - } - - m_DeviceOpened = true; - PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName << "' opened successfully"); - return true; -} - -bool PcapNgFileWriterDevice::writePacket(RawPacket const& packet, const std::string& comment) -{ - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Device not opened"); - m_NumOfPacketsNotWritten++; - return false; - } - - if (!m_BpfWrapper.matchPacketWithFilter(&packet)) - { - return false; - } - - light_packet_header pktHeader; - pktHeader.captured_length = ((RawPacket&)packet).getRawDataLen(); - pktHeader.original_length = ((RawPacket&)packet).getFrameLength(); - pktHeader.timestamp = ((RawPacket&)packet).getPacketTimeStamp(); - pktHeader.data_link = (uint16_t)packet.getLinkLayerType(); - pktHeader.interface_id = 0; - if (!comment.empty()) - { - pktHeader.comment = (char*)comment.c_str(); - pktHeader.comment_length = static_cast(comment.size()); - } - else - { - pktHeader.comment = nullptr; - pktHeader.comment_length = 0; - } - - const uint8_t* pktData = ((RawPacket&)packet).getRawData(); - - light_write_packet((light_pcapng_t*)m_LightPcapNg, &pktHeader, pktData); - m_NumOfPacketsWritten++; - return true; -} - -bool PcapNgFileWriterDevice::writePacket(RawPacket const& packet) -{ - return writePacket(packet, std::string()); -} - -bool PcapNgFileWriterDevice::writePackets(const RawPacketVector& packets) -{ - for (RawPacketVector::ConstVectorIterator iter = packets.begin(); iter != packets.end(); iter++) - { - if (!writePacket(**iter)) - return false; - } - - return true; -} - -bool PcapNgFileWriterDevice::open() -{ - if (m_LightPcapNg != nullptr) - { - PCPP_LOG_DEBUG("Pcap-ng descriptor already opened. Nothing to do"); - return true; - } - - m_NumOfPacketsNotWritten = 0; - m_NumOfPacketsWritten = 0; - - light_pcapng_file_info* info = light_create_default_file_info(); - - m_LightPcapNg = light_pcapng_open_write(m_FileName.c_str(), info, m_CompressionLevel); - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName << "': light_pcapng_open_write returned NULL"); - - light_free_file_info(info); - - m_DeviceOpened = false; - return false; - } - - m_DeviceOpened = true; - PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName << "' opened successfully"); - return true; -} - -bool PcapNgFileWriterDevice::open(bool appendMode) -{ - if (!appendMode) - return open(); - - m_NumOfPacketsNotWritten = 0; - m_NumOfPacketsWritten = 0; - - m_LightPcapNg = light_pcapng_open_append(m_FileName.c_str()); - if (m_LightPcapNg == nullptr) - { - PCPP_LOG_ERROR("Error opening file writer device in append mode for file '" << m_FileName << "': light_pcapng_open_append returned NULL"); - m_DeviceOpened = false; - return false; - } - - m_DeviceOpened = true; - PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName << "' opened successfully"); - return true; - -} - -void PcapNgFileWriterDevice::flush() -{ - if (!m_DeviceOpened || m_LightPcapNg == nullptr) - return; - - light_pcapng_flush((light_pcapng_t*)m_LightPcapNg); - PCPP_LOG_DEBUG("File writer flushed to file '" << m_FileName << "'"); -} - -void PcapNgFileWriterDevice::close() -{ - if (m_LightPcapNg == nullptr) - return; - - light_pcapng_close((light_pcapng_t*)m_LightPcapNg); - m_LightPcapNg = nullptr; - - m_DeviceOpened = false; - PCPP_LOG_DEBUG("File writer closed for file '" << m_FileName << "'"); -} - -void PcapNgFileWriterDevice::getStatistics(PcapStats& stats) const -{ - stats.packetsRecv = m_NumOfPacketsWritten; - stats.packetsDrop = m_NumOfPacketsNotWritten; - stats.packetsDropByInterface = 0; - PCPP_LOG_DEBUG("Statistics received for pcap-ng writer device for filename '" << m_FileName << "'"); -} - -bool PcapNgFileWriterDevice::setFilter(std::string filterAsString) -{ - return m_BpfWrapper.setFilter(filterAsString); -} - - -} // namespace pcpp +#define LOG_MODULE PcapLogModuleFileDevice + +#include +#include +#include "PcapFileDevice.h" +#include "light_pcapng_ext.h" +#include "Logger.h" +#include "TimespecTimeval.h" +#include "pcap.h" +#include +#include +#include "EndianPortable.h" + +namespace pcpp +{ + +template +constexpr size_t ARRAY_SIZE(T (&)[N]) { return N; } + +struct pcap_file_header +{ + uint32_t magic; + uint16_t version_major; + uint16_t version_minor; + int32_t thiszone; + uint32_t sigfigs; + uint32_t snaplen; + uint32_t linktype; +}; + +struct packet_header +{ + uint32_t tv_sec; + uint32_t tv_usec; + uint32_t caplen; + uint32_t len; +}; + +// ~~~~~~~~~~~~~~~~~~~ +// IFileDevice members +// ~~~~~~~~~~~~~~~~~~~ + +IFileDevice::IFileDevice(const std::string& fileName) : IPcapDevice() +{ + m_FileName = fileName; +} + +IFileDevice::~IFileDevice() +{ + IFileDevice::close(); +} + +std::string IFileDevice::getFileName() const +{ + return m_FileName; +} + +void IFileDevice::close() +{ + if (m_PcapDescriptor != nullptr) + { + pcap_close(m_PcapDescriptor); + PCPP_LOG_DEBUG("Successfully closed file reader device for filename '" << m_FileName << "'"); + m_PcapDescriptor = nullptr; + } + + m_DeviceOpened = false; +} + + +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// IFileReaderDevice members +// ~~~~~~~~~~~~~~~~~~~~~~~~~ + +IFileReaderDevice::IFileReaderDevice(const std::string& fileName) : IFileDevice(fileName) +{ + m_NumOfPacketsNotParsed = 0; + m_NumOfPacketsRead = 0; +} + +IFileReaderDevice* IFileReaderDevice::getReader(const std::string& fileName) +{ + const auto extensionPos = fileName.find_last_of('.'); + const auto fileExtension = extensionPos != std::string::npos ? fileName.substr(extensionPos) : ""; + + if (fileExtension == ".pcapng" || fileExtension == ".zstd" || fileExtension == ".zst") + return new PcapNgFileReaderDevice(fileName); + else if (fileExtension == ".snoop") + return new SnoopFileReaderDevice(fileName); + + return new PcapFileReaderDevice(fileName); +} + +uint64_t IFileReaderDevice::getFileSize() const +{ + std::ifstream fileStream(m_FileName.c_str(), std::ifstream::ate | std::ifstream::binary); + return fileStream.tellg(); +} + +int IFileReaderDevice::getNextPackets(RawPacketVector& packetVec, int numOfPacketsToRead) +{ + int numOfPacketsRead = 0; + + for (; numOfPacketsToRead < 0 || numOfPacketsRead < numOfPacketsToRead; numOfPacketsRead++) + { + RawPacket* newPacket = new RawPacket(); + bool packetRead = getNextPacket(*newPacket); + if (packetRead) + { + packetVec.pushBack(newPacket); + } + else + { + delete newPacket; + break; + } + } + + return numOfPacketsRead; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// SnoopFileReaderDevice members +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +SnoopFileReaderDevice::~SnoopFileReaderDevice() +{ + m_snoopFile.close(); +} + +bool SnoopFileReaderDevice::open() +{ + m_NumOfPacketsRead = 0; + m_NumOfPacketsNotParsed = 0; + + m_snoopFile.open(m_FileName.c_str(), std::ifstream::binary); + if (!m_snoopFile.is_open()) + { + PCPP_LOG_ERROR("Cannot open snoop reader device for filename '" << m_FileName << "'"); + m_snoopFile.close(); + return false; + } + + snoop_file_header_t snoop_file_header; + m_snoopFile.read((char*)&snoop_file_header, sizeof(snoop_file_header_t)); + if (!m_snoopFile) + { + PCPP_LOG_ERROR("Cannot read snoop file header for '" << m_FileName << "'"); + m_snoopFile.close(); + return false; + } + + if(be64toh(snoop_file_header.identification_pattern) != 0x736e6f6f70000000 && be32toh(snoop_file_header.version_number) == 2) + return false; + + // From https://datatracker.ietf.org/doc/html/rfc1761 + static const pcpp::LinkLayerType snoop_encap[] = { + LINKTYPE_ETHERNET, /* IEEE 802.3 */ + LINKTYPE_NULL, /* IEEE 802.4 Token Bus */ + LINKTYPE_IEEE802_5, /* IEEE 802.5 */ + LINKTYPE_NULL, /* IEEE 802.6 Metro Net */ + LINKTYPE_ETHERNET, /* Ethernet */ + LINKTYPE_C_HDLC, /* HDLC */ + LINKTYPE_NULL, /* Character Synchronous, e.g. bisync */ + LINKTYPE_NULL, /* IBM Channel-to-Channel */ + LINKTYPE_FDDI /* FDDI */ + }; + uint32_t datalink_type = be32toh(snoop_file_header.datalink_type); + if (datalink_type > ARRAY_SIZE(snoop_encap) - 1) + { + PCPP_LOG_ERROR("Cannot read data link type for '" << m_FileName << "'"); + m_snoopFile.close(); + return false; + } + + m_PcapLinkLayerType = snoop_encap[datalink_type]; + + PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" << m_FileName << "'"); + m_DeviceOpened = true; + return true; +} + +void SnoopFileReaderDevice::getStatistics(PcapStats& stats) const +{ + stats.packetsRecv = m_NumOfPacketsRead; + stats.packetsDrop = m_NumOfPacketsNotParsed; + stats.packetsDropByInterface = 0; + PCPP_LOG_DEBUG("Statistics received for reader device for filename '" << m_FileName << "'"); +} + +bool SnoopFileReaderDevice::getNextPacket(RawPacket& rawPacket) +{ + rawPacket.clear(); + if (m_DeviceOpened != true) + { + PCPP_LOG_ERROR("File device '" << m_FileName << "' not opened"); + return false; + } + snoop_packet_header_t snoop_packet_header; + m_snoopFile.read((char*)&snoop_packet_header, sizeof(snoop_packet_header_t)); + if(!m_snoopFile) { + return false; + } + size_t packetSize = be32toh(snoop_packet_header.included_length); + if(packetSize > 15000) { + return false; + } + char* packetData = new char[packetSize]; + m_snoopFile.read(packetData, packetSize); + if(!m_snoopFile) { + return false; + } + timespec ts = { static_cast(be32toh(snoop_packet_header.time_sec)), static_cast(be32toh(snoop_packet_header.time_usec)) * 1000 }; + if (!rawPacket.setRawData((const uint8_t*)packetData, packetSize, ts, static_cast(m_PcapLinkLayerType))) + { + PCPP_LOG_ERROR("Couldn't set data to raw packet"); + return false; + } + size_t pad = be32toh(snoop_packet_header.packet_record_length) - (sizeof(snoop_packet_header_t) + be32toh(snoop_packet_header.included_length)); + m_snoopFile.ignore(pad); + if(!m_snoopFile) { + return false; + } + + m_NumOfPacketsRead++; + return true; +} + +void SnoopFileReaderDevice::close() +{ + m_snoopFile.close(); + m_DeviceOpened = false; + PCPP_LOG_DEBUG("File reader closed for file '" << m_FileName << "'"); +} + + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// PcapFileReaderDevice members +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +bool PcapFileReaderDevice::open() +{ + m_NumOfPacketsRead = 0; + m_NumOfPacketsNotParsed = 0; + + if (m_PcapDescriptor != nullptr) + { + PCPP_LOG_DEBUG("Pcap descriptor already opened. Nothing to do"); + return true; + } + + char errbuf[PCAP_ERRBUF_SIZE]; +#if defined(PCAP_TSTAMP_PRECISION_NANO) + m_PcapDescriptor = pcap_open_offline_with_tstamp_precision(m_FileName.c_str(), PCAP_TSTAMP_PRECISION_NANO, errbuf); +#else + m_PcapDescriptor = pcap_open_offline(m_FileName.c_str(), errbuf); +#endif + if (m_PcapDescriptor == nullptr) + { + PCPP_LOG_ERROR("Cannot open file reader device for filename '" << m_FileName << "': " << errbuf); + m_DeviceOpened = false; + return false; + } + + int linkLayer = pcap_datalink(m_PcapDescriptor); + if (!RawPacket::isLinkTypeValid(linkLayer)) + { + PCPP_LOG_ERROR("Invalid link layer (" << linkLayer << ") for reader device filename '" << m_FileName << "'"); + pcap_close(m_PcapDescriptor); + m_PcapDescriptor = nullptr; + m_DeviceOpened = false; + return false; + } + + m_PcapLinkLayerType = static_cast(linkLayer); + + PCPP_LOG_DEBUG("Successfully opened file reader device for filename '" << m_FileName << "'"); + m_DeviceOpened = true; + return true; +} + +void PcapFileReaderDevice::getStatistics(PcapStats& stats) const +{ + stats.packetsRecv = m_NumOfPacketsRead; + stats.packetsDrop = m_NumOfPacketsNotParsed; + stats.packetsDropByInterface = 0; + PCPP_LOG_DEBUG("Statistics received for reader device for filename '" << m_FileName << "'"); +} + +bool PcapFileReaderDevice::getNextPacket(RawPacket& rawPacket) +{ + rawPacket.clear(); + if (m_PcapDescriptor == nullptr) + { + PCPP_LOG_ERROR("File device '" << m_FileName << "' not opened"); + return false; + } + pcap_pkthdr pkthdr; + const uint8_t* pPacketData = pcap_next(m_PcapDescriptor, &pkthdr); + if (pPacketData == nullptr) + { + PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file"); + return false; + } + + uint8_t* pMyPacketData = new uint8_t[pkthdr.caplen]; + memcpy(pMyPacketData, pPacketData, pkthdr.caplen); +#if defined(PCAP_TSTAMP_PRECISION_NANO) + timespec ts = { pkthdr.ts.tv_sec, static_cast(pkthdr.ts.tv_usec) }; //because we opened with nano second precision 'tv_usec' is actually nanos +#else + struct timeval ts = pkthdr.ts; +#endif + if (!rawPacket.setRawData(pMyPacketData, pkthdr.caplen, ts, static_cast(m_PcapLinkLayerType), pkthdr.len)) + { + PCPP_LOG_ERROR("Couldn't set data to raw packet"); + return false; + } + m_NumOfPacketsRead++; + return true; +} + + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// PcapNgFileReaderDevice members +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +PcapNgFileReaderDevice::PcapNgFileReaderDevice(const std::string& fileName) : IFileReaderDevice(fileName) +{ + m_LightPcapNg = nullptr; +} + +bool PcapNgFileReaderDevice::open() +{ + m_NumOfPacketsRead = 0; + m_NumOfPacketsNotParsed = 0; + + if (m_LightPcapNg != nullptr) + { + PCPP_LOG_DEBUG("pcapng descriptor already opened. Nothing to do"); + return true; + } + + m_LightPcapNg = light_pcapng_open_read(m_FileName.c_str(), LIGHT_FALSE); + if (m_LightPcapNg == nullptr) + { + PCPP_LOG_ERROR("Cannot open pcapng reader device for filename '" << m_FileName << "'"); + m_DeviceOpened = false; + return false; + } + + PCPP_LOG_DEBUG("Successfully opened pcapng reader device for filename '" << m_FileName << "'"); + m_DeviceOpened = true; + return true; +} + +bool PcapNgFileReaderDevice::getNextPacket(RawPacket& rawPacket, std::string& packetComment) +{ + rawPacket.clear(); + packetComment = ""; + + if (m_LightPcapNg == nullptr) + { + PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); + return false; + } + + light_packet_header pktHeader; + const uint8_t* pktData = nullptr; + + if (!light_get_next_packet((light_pcapng_t*)m_LightPcapNg, &pktHeader, &pktData)) + { + PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file"); + return false; + } + + while (!m_BpfWrapper.matchPacketWithFilter(pktData, pktHeader.captured_length, pktHeader.timestamp, pktHeader.data_link)) + { + if (!light_get_next_packet((light_pcapng_t*)m_LightPcapNg, &pktHeader, &pktData)) + { + PCPP_LOG_DEBUG("Packet could not be read. Probably end-of-file"); + return false; + } + } + + uint8_t* myPacketData = new uint8_t[pktHeader.captured_length]; + memcpy(myPacketData, pktData, pktHeader.captured_length); + if (!rawPacket.setRawData(myPacketData, pktHeader.captured_length, pktHeader.timestamp, static_cast(pktHeader.data_link), pktHeader.original_length)) + { + PCPP_LOG_ERROR("Couldn't set data to raw packet"); + return false; + } + + if (pktHeader.comment != nullptr && pktHeader.comment_length > 0) + packetComment = std::string(pktHeader.comment, pktHeader.comment_length); + + m_NumOfPacketsRead++; + return true; +} + +bool PcapNgFileReaderDevice::getNextPacket(RawPacket& rawPacket) +{ + std::string temp; + return getNextPacket(rawPacket, temp); +} + +void PcapNgFileReaderDevice::getStatistics(PcapStats& stats) const +{ + stats.packetsRecv = m_NumOfPacketsRead; + stats.packetsDrop = m_NumOfPacketsNotParsed; + stats.packetsDropByInterface = 0; + PCPP_LOG_DEBUG("Statistics received for pcapng reader device for filename '" << m_FileName << "'"); +} + +bool PcapNgFileReaderDevice::setFilter(std::string filterAsString) +{ + return m_BpfWrapper.setFilter(filterAsString); +} + +void PcapNgFileReaderDevice::close() +{ + if (m_LightPcapNg == nullptr) + return; + + light_pcapng_close((light_pcapng_t*)m_LightPcapNg); + m_LightPcapNg = nullptr; + + m_DeviceOpened = false; + PCPP_LOG_DEBUG("File reader closed for file '" << m_FileName << "'"); +} + + +std::string PcapNgFileReaderDevice::getOS() const +{ + if (m_LightPcapNg == nullptr) + { + PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); + return ""; + } + + light_pcapng_file_info* fileInfo = light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); + char* res = fileInfo->os_desc; + size_t len = fileInfo->os_desc_size; + if (len == 0 || res == nullptr) + return ""; + + return std::string(res, len); +} + +std::string PcapNgFileReaderDevice::getHardware() const +{ + if (m_LightPcapNg == nullptr) + { + PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); + return ""; + } + + light_pcapng_file_info* fileInfo = light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); + char* res = fileInfo->hardware_desc; + size_t len = fileInfo->hardware_desc_size; + if (len == 0 || res == nullptr) + return ""; + + return std::string(res, len); +} + +std::string PcapNgFileReaderDevice::getCaptureApplication() const +{ + if (m_LightPcapNg == nullptr) + { + PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); + return ""; + } + + light_pcapng_file_info* fileInfo = light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); + char* res = fileInfo->user_app_desc; + size_t len = fileInfo->user_app_desc_size; + if (len == 0 || res == nullptr) + return ""; + + return std::string(res, len); +} + +std::string PcapNgFileReaderDevice::getCaptureFileComment() const +{ + if (m_LightPcapNg == nullptr) + { + PCPP_LOG_ERROR("Pcapng file device '" << m_FileName << "' not opened"); + return ""; + } + + light_pcapng_file_info* fileInfo = light_pcang_get_file_info((light_pcapng_t*)m_LightPcapNg); + char* res = fileInfo->file_comment; + size_t len = fileInfo->file_comment_size; + if (len == 0 || res == nullptr) + return ""; + + return std::string(res, len); +} + + +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// IFileWriterDevice members +// ~~~~~~~~~~~~~~~~~~~~~~~~~ + +IFileWriterDevice:: IFileWriterDevice(const std::string& fileName) : IFileDevice(fileName) +{ + m_NumOfPacketsNotWritten = 0; + m_NumOfPacketsWritten = 0; +} + + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// PcapFileWriterDevice members +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +PcapFileWriterDevice::PcapFileWriterDevice(const std::string& fileName, LinkLayerType linkLayerType) : IFileWriterDevice(fileName) +{ + m_PcapDumpHandler = nullptr; + m_NumOfPacketsNotWritten = 0; + m_NumOfPacketsWritten = 0; + m_PcapLinkLayerType = linkLayerType; + m_AppendMode = false; + m_File = nullptr; +} + +void PcapFileWriterDevice::closeFile() +{ + if (m_AppendMode && m_File != nullptr) + { + fclose(m_File); + m_File = nullptr; + } +} + +bool PcapFileWriterDevice::writePacket(RawPacket const& packet) +{ + if ((!m_AppendMode && m_PcapDescriptor == nullptr) || (m_PcapDumpHandler == nullptr)) + { + PCPP_LOG_ERROR("Device not opened"); + m_NumOfPacketsNotWritten++; + return false; + } + + if (packet.getLinkLayerType() != m_PcapLinkLayerType) + { + PCPP_LOG_ERROR("Cannot write a packet with a different link layer type"); + m_NumOfPacketsNotWritten++; + return false; + } + + pcap_pkthdr pktHdr; + pktHdr.caplen = ((RawPacket&)packet).getRawDataLen(); + pktHdr.len = ((RawPacket&)packet).getFrameLength(); + timespec packet_timestamp = ((RawPacket&)packet).getPacketTimeStamp(); + TIMESPEC_TO_TIMEVAL(&pktHdr.ts, &packet_timestamp); + if (!m_AppendMode) + pcap_dump((uint8_t*)m_PcapDumpHandler, &pktHdr, ((RawPacket&)packet).getRawData()); + else + { + // Below are actually the lines run by pcap_dump. The reason I had to put them instead pcap_dump is that on Windows using WinPcap/Npcap + // you can't pass pointers between libraries compiled with different compilers. In this case - PcapPlusPlus and WinPcap/Npcap weren't + // compiled with the same compiler so it's impossible to fopen a file in PcapPlusPlus, pass the pointer to WinPcap/Npcap and use the + // FILE* pointer there. Doing this throws an exception. So the only option when implementing append to pcap is to write all relevant + // WinPcap/Npcap code that handles opening/closing/writing to pcap files inside PcapPlusPlus code + + // the reason to create this packet_header struct is timeval has different sizes in 32-bit and 64-bit systems, + // but pcap format uses the 32-bit timeval version, so we need to align timeval to that + packet_header pktHdrTemp; + pktHdrTemp.tv_sec = pktHdr.ts.tv_sec; + pktHdrTemp.tv_usec = pktHdr.ts.tv_usec; + pktHdrTemp.caplen = pktHdr.caplen; + pktHdrTemp.len = pktHdr.len; + fwrite(&pktHdrTemp, sizeof(pktHdrTemp), 1, m_File); + fwrite(((RawPacket&)packet).getRawData(), pktHdrTemp.caplen, 1, m_File); + } + PCPP_LOG_DEBUG("Packet written successfully to '" << m_FileName << "'"); + m_NumOfPacketsWritten++; + return true; +} + +bool PcapFileWriterDevice::writePackets(const RawPacketVector& packets) +{ + for (RawPacketVector::ConstVectorIterator iter = packets.begin(); iter != packets.end(); iter++) + { + if (!writePacket(**iter)) + return false; + } + + return true; +} + +bool PcapFileWriterDevice::open() +{ + if (m_PcapDescriptor != nullptr) + { + PCPP_LOG_DEBUG("Pcap descriptor already opened. Nothing to do"); + return true; + } + + switch(m_PcapLinkLayerType) + { + case LINKTYPE_RAW: + case LINKTYPE_DLT_RAW2: + PCPP_LOG_ERROR("The only Raw IP link type supported in libpcap/WinPcap/Npcap is LINKTYPE_DLT_RAW1, please use that instead"); + return false; + default: + break; + } + + m_NumOfPacketsNotWritten = 0; + m_NumOfPacketsWritten = 0; + + m_PcapDescriptor = pcap_open_dead(m_PcapLinkLayerType, PCPP_MAX_PACKET_SIZE); + if (m_PcapDescriptor == nullptr) + { + PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName << "': pcap_open_dead returned NULL"); + m_DeviceOpened = false; + return false; + } + + + m_PcapDumpHandler = pcap_dump_open(m_PcapDescriptor, m_FileName.c_str()); + if (m_PcapDumpHandler == nullptr) + { + PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName << "': pcap_dump_open returned NULL with error: '" << pcap_geterr(m_PcapDescriptor) << "'"); + m_DeviceOpened = false; + return false; + } + + m_DeviceOpened = true; + PCPP_LOG_DEBUG("File writer device for file '" << m_FileName << "' opened successfully"); + return true; +} + +void PcapFileWriterDevice::flush() +{ + if (!m_DeviceOpened) + return; + + if (!m_AppendMode && pcap_dump_flush(m_PcapDumpHandler) == -1) + { + PCPP_LOG_ERROR("Error while flushing the packets to file"); + } + // in append mode it's impossible to use pcap_dump_flush, see comment above pcap_dump + else if (m_AppendMode && fflush(m_File) == EOF) + { + PCPP_LOG_ERROR("Error while flushing the packets to file"); + } + +} + +void PcapFileWriterDevice::close() +{ + if (!m_DeviceOpened) + return; + + flush(); + + IFileDevice::close(); + + if (!m_AppendMode && m_PcapDumpHandler != nullptr) + { + pcap_dump_close(m_PcapDumpHandler); + } + else if (m_AppendMode && m_File != nullptr) + { + // in append mode it's impossible to use pcap_dump_close, see comment above pcap_dump + fclose(m_File); + } + + m_PcapDumpHandler = nullptr; + m_File = nullptr; + PCPP_LOG_DEBUG("File writer closed for file '" << m_FileName << "'"); +} + +void PcapFileWriterDevice::getStatistics(PcapStats& stats) const +{ + stats.packetsRecv = m_NumOfPacketsWritten; + stats.packetsDrop = m_NumOfPacketsNotWritten; + stats.packetsDropByInterface = 0; + PCPP_LOG_DEBUG("Statistics received for writer device for filename '" << m_FileName << "'"); +} + +bool PcapFileWriterDevice::open(bool appendMode) +{ + if (!appendMode) + return open(); + + m_AppendMode = appendMode; + +#if !defined(_WIN32) + m_File = fopen(m_FileName.c_str(), "r+"); +#else + m_File = fopen(m_FileName.c_str(), "rb+"); +#endif + + if (m_File == nullptr) + { + PCPP_LOG_ERROR("Cannot open '" << m_FileName << "' for reading and writing"); + return false; + } + + pcap_file_header pcapFileHeader; + int amountRead = fread(&pcapFileHeader, 1, sizeof(pcapFileHeader), m_File); + if (amountRead != sizeof(pcap_file_header)) + { + if (ferror(m_File)) + PCPP_LOG_ERROR("Cannot read pcap header from file '" << m_FileName << "', error was: " << errno); + else + PCPP_LOG_ERROR("Cannot read pcap header from file '" << m_FileName << "', unknown error"); + + closeFile(); + return false; + } + + LinkLayerType linkLayerType = static_cast(pcapFileHeader.linktype); + if (linkLayerType != m_PcapLinkLayerType) + { + PCPP_LOG_ERROR("Pcap file has a different link layer type than the one chosen in PcapFileWriterDevice c'tor, " << linkLayerType << ", " << m_PcapLinkLayerType); + closeFile(); + return false; + } + + if (fseek(m_File, 0, SEEK_END) == -1) + { + PCPP_LOG_ERROR("Cannot read pcap file '" << m_FileName << "' to it's end, error was: " << errno); + closeFile(); + return false; + } + + m_PcapDumpHandler = ((pcap_dumper_t *)m_File); + + m_DeviceOpened = true; + PCPP_LOG_DEBUG("File writer device for file '" << m_FileName << "' opened successfully in append mode"); + return true; +} + + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// PcapNgFileWriterDevice members +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +PcapNgFileWriterDevice::PcapNgFileWriterDevice(const std::string& fileName, int compressionLevel) : IFileWriterDevice(fileName) +{ + m_LightPcapNg = nullptr; + m_CompressionLevel = compressionLevel; +} + +bool PcapNgFileWriterDevice::open(const std::string& os, const std::string& hardware, const std::string& captureApp, const std::string& fileComment) +{ + if (m_LightPcapNg != nullptr) + { + PCPP_LOG_DEBUG("Pcap-ng descriptor already opened. Nothing to do"); + return true; + } + + m_NumOfPacketsNotWritten = 0; + m_NumOfPacketsWritten = 0; + + light_pcapng_file_info* info = light_create_file_info(os.c_str(), hardware.c_str(), captureApp.c_str(), fileComment.c_str()); + + m_LightPcapNg = light_pcapng_open_write(m_FileName.c_str(), info, m_CompressionLevel); + if (m_LightPcapNg == nullptr) + { + PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName << "': light_pcapng_open_write returned NULL"); + + light_free_file_info(info); + + m_DeviceOpened = false; + return false; + } + + m_DeviceOpened = true; + PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName << "' opened successfully"); + return true; +} + +bool PcapNgFileWriterDevice::writePacket(RawPacket const& packet, const std::string& comment) +{ + if (m_LightPcapNg == nullptr) + { + PCPP_LOG_ERROR("Device not opened"); + m_NumOfPacketsNotWritten++; + return false; + } + + if (!m_BpfWrapper.matchPacketWithFilter(&packet)) + { + return false; + } + + light_packet_header pktHeader; + pktHeader.captured_length = ((RawPacket&)packet).getRawDataLen(); + pktHeader.original_length = ((RawPacket&)packet).getFrameLength(); + pktHeader.timestamp = ((RawPacket&)packet).getPacketTimeStamp(); + pktHeader.data_link = (uint16_t)packet.getLinkLayerType(); + pktHeader.interface_id = 0; + if (!comment.empty()) + { + pktHeader.comment = (char*)comment.c_str(); + pktHeader.comment_length = static_cast(comment.size()); + } + else + { + pktHeader.comment = nullptr; + pktHeader.comment_length = 0; + } + + const uint8_t* pktData = ((RawPacket&)packet).getRawData(); + + light_write_packet((light_pcapng_t*)m_LightPcapNg, &pktHeader, pktData); + m_NumOfPacketsWritten++; + return true; +} + +bool PcapNgFileWriterDevice::writePacket(RawPacket const& packet) +{ + return writePacket(packet, std::string()); +} + +bool PcapNgFileWriterDevice::writePackets(const RawPacketVector& packets) +{ + for (RawPacketVector::ConstVectorIterator iter = packets.begin(); iter != packets.end(); iter++) + { + if (!writePacket(**iter)) + return false; + } + + return true; +} + +bool PcapNgFileWriterDevice::open() +{ + if (m_LightPcapNg != nullptr) + { + PCPP_LOG_DEBUG("Pcap-ng descriptor already opened. Nothing to do"); + return true; + } + + m_NumOfPacketsNotWritten = 0; + m_NumOfPacketsWritten = 0; + + light_pcapng_file_info* info = light_create_default_file_info(); + + m_LightPcapNg = light_pcapng_open_write(m_FileName.c_str(), info, m_CompressionLevel); + if (m_LightPcapNg == nullptr) + { + PCPP_LOG_ERROR("Error opening file writer device for file '" << m_FileName << "': light_pcapng_open_write returned NULL"); + + light_free_file_info(info); + + m_DeviceOpened = false; + return false; + } + + m_DeviceOpened = true; + PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName << "' opened successfully"); + return true; +} + +bool PcapNgFileWriterDevice::open(bool appendMode) +{ + if (!appendMode) + return open(); + + m_NumOfPacketsNotWritten = 0; + m_NumOfPacketsWritten = 0; + + m_LightPcapNg = light_pcapng_open_append(m_FileName.c_str()); + if (m_LightPcapNg == nullptr) + { + PCPP_LOG_ERROR("Error opening file writer device in append mode for file '" << m_FileName << "': light_pcapng_open_append returned NULL"); + m_DeviceOpened = false; + return false; + } + + m_DeviceOpened = true; + PCPP_LOG_DEBUG("pcap-ng writer device for file '" << m_FileName << "' opened successfully"); + return true; + +} + +void PcapNgFileWriterDevice::flush() +{ + if (!m_DeviceOpened || m_LightPcapNg == nullptr) + return; + + light_pcapng_flush((light_pcapng_t*)m_LightPcapNg); + PCPP_LOG_DEBUG("File writer flushed to file '" << m_FileName << "'"); +} + +void PcapNgFileWriterDevice::close() +{ + if (m_LightPcapNg == nullptr) + return; + + light_pcapng_close((light_pcapng_t*)m_LightPcapNg); + m_LightPcapNg = nullptr; + + m_DeviceOpened = false; + PCPP_LOG_DEBUG("File writer closed for file '" << m_FileName << "'"); +} + +void PcapNgFileWriterDevice::getStatistics(PcapStats& stats) const +{ + stats.packetsRecv = m_NumOfPacketsWritten; + stats.packetsDrop = m_NumOfPacketsNotWritten; + stats.packetsDropByInterface = 0; + PCPP_LOG_DEBUG("Statistics received for pcap-ng writer device for filename '" << m_FileName << "'"); +} + +bool PcapNgFileWriterDevice::setFilter(std::string filterAsString) +{ + return m_BpfWrapper.setFilter(filterAsString); +} + + +} // namespace pcpp diff --git a/Pcap++/src/PcapFilter.cpp b/Pcap++/src/PcapFilter.cpp index bfaccb74bc..758da2fd9a 100644 --- a/Pcap++/src/PcapFilter.cpp +++ b/Pcap++/src/PcapFilter.cpp @@ -1,463 +1,463 @@ -#define LOG_MODULE PcapLogModuleLiveDevice - -#include "PcapFilter.h" -#include "Logger.h" -#include "IPv4Layer.h" -#include -#if defined(_WIN32) -#include -#endif -#include "pcap.h" -#include "RawPacket.h" -#include "TimespecTimeval.h" - -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); -} - -BpfFilterWrapper::BpfFilterWrapper() -{ - m_Program = nullptr; - m_LinkType = LINKTYPE_ETHERNET; -} - -BpfFilterWrapper::~BpfFilterWrapper() -{ - freeProgram(); -} - -bool BpfFilterWrapper::setFilter(const std::string& filter, LinkLayerType linkType) -{ - if (filter.empty()) - { - freeProgram(); - return true; - } - - if (filter != m_FilterStr || linkType != m_LinkType) - { - bpf_program* newProg = new bpf_program; - if (pcap_compile_nopcap(DEFAULT_SNAPLEN, linkType, newProg, filter.c_str(), 1, 0) < 0) - { - delete newProg; - return false; - } - else - { - freeProgram(); - m_Program = newProg; - m_FilterStr = filter; - m_LinkType = linkType; - } - } - - return true; -} - -void BpfFilterWrapper::freeProgram() -{ - if (m_Program != nullptr) - { - pcap_freecode(m_Program); - delete m_Program; - m_Program = nullptr; - m_FilterStr.clear(); - } -} - -bool BpfFilterWrapper::matchPacketWithFilter(const RawPacket* rawPacket) -{ - return matchPacketWithFilter(rawPacket->getRawData(), rawPacket->getRawDataLen(), rawPacket->getPacketTimeStamp(), rawPacket->getLinkLayerType()); -} - -bool BpfFilterWrapper::matchPacketWithFilter(const uint8_t* packetData, uint32_t packetDataLength, timespec packetTimestamp, uint16_t linkType) -{ - if (m_FilterStr.empty()) - return true; - - if (!setFilter(std::string(m_FilterStr), static_cast(linkType))) - { - return false; - } - - struct pcap_pkthdr pktHdr; - pktHdr.caplen = packetDataLength; - pktHdr.len = packetDataLength; - TIMESPEC_TO_TIMEVAL(&pktHdr.ts, &packetTimestamp); - - return (pcap_offline_filter(m_Program, &pktHdr, packetData) != 0); -} - -void BPFStringFilter::parseToString(std::string& result) -{ - result = m_FilterStr; -} - -bool BPFStringFilter::verifyFilter() -{ - return m_BpfWrapper.setFilter(m_FilterStr); -} - -void IFilterWithDirection::parseDirection(std::string& directionAsString) -{ - switch (m_Dir) - { - case SRC: - directionAsString = "src"; - break; - case DST: - directionAsString = "dst"; - break; - default: //SRC_OR_DST: - directionAsString = "src or dst"; - break; - } -} - -std::string IFilterWithOperator::parseOperator() -{ - switch(m_Operator) - { - case EQUALS: - return "="; - case NOT_EQUALS: - return "!="; - case GREATER_THAN: - return ">"; - case GREATER_OR_EQUAL: - return ">="; - case LESS_THAN: - return "<"; - case LESS_OR_EQUAL: - return "<="; - default: - return ""; - } -} - -void IPFilter::convertToIPAddressWithMask(std::string& ipAddrmodified, std::string& mask) const -{ - if (m_IPv4Mask.empty()) - return; - - // Handle the mask - - // The following code lines verify both ipAddress and ipv4Mask are valid IPv4 addresses - // The IPv4 limitation comes from the fact libPcap/WinPcap/Npcap doesn't support mask for IPv6 addresses - - IPv4Address ipAddr(m_Address); - if (!ipAddr.isValid()) - { - PCPP_LOG_ERROR("IP filter with mask must be used with IPv4 valid address. Setting the mask to an empty value"); - mask.clear(); - return; - } - - IPv4Address maskAsAddr(m_IPv4Mask); - if (!maskAsAddr.isValid()) - { - PCPP_LOG_ERROR("Invalid IPv4 mask. Setting the mask to an empty"); - mask.clear(); - return; - } - - // If all addresses are IPv4 valid addresses, make sure ipAddress matches the mask. If it's not, mask the address with the mask - // The reason for doing that is libPcap/WinPcap/Npcap doesn't allow filtering an IP address that doesn't match the mask - - uint32_t addrAsIntAfterMask = ipAddr.toInt() & maskAsAddr.toInt(); - ipAddrmodified = IPv4Address(addrAsIntAfterMask).toString(); -} - -void IPFilter::convertToIPAddressWithLen(std::string& ipAddrmodified) const -{ - if (m_Len == 0) - return; - - // Handle the length - - // The following code lines verify IP address is valid (IPv4 or IPv6) - - IPAddress ipAddr = IPAddress(ipAddrmodified); - if (!ipAddr.isValid()) - { - PCPP_LOG_ERROR("Invalid IP address '" << ipAddrmodified << "', setting len to zero"); - return; - } - - if (ipAddr.getType() == IPAddress::IPv4AddressType) - { - uint32_t addrAsInt = ipAddr.getIPv4().toInt(); - uint32_t mask = ((uint32_t) - 1) >> ((sizeof(uint32_t) * 8) - m_Len); - addrAsInt &= mask; - ipAddrmodified = IPv4Address(addrAsInt).toString(); - } -} - -void IPFilter::parseToString(std::string& result) -{ - std::string dir; - std::string ipAddr = m_Address; - std::string mask = m_IPv4Mask; - convertToIPAddressWithMask(ipAddr, mask); - convertToIPAddressWithLen(ipAddr); - parseDirection(dir); - result = "ip and " + dir + " net " + ipAddr; - if (m_IPv4Mask != "") - result += " mask " + mask; - else if (m_Len > 0) - { - std::ostringstream stream; - stream << m_Len; - result += '/' + stream.str(); - } -} - -void IPv4IDFilter::parseToString(std::string& result) -{ - std::string op = parseOperator(); - std::ostringstream stream; - stream << m_IpID; - result = "ip[4:2] " + op + ' ' + stream.str(); -} - -void IPv4TotalLengthFilter::parseToString(std::string& result) -{ - std::string op = parseOperator(); - std::ostringstream stream; - stream << m_TotalLength; - result = "ip[2:2] " + op + ' ' + stream.str(); -} - -void PortFilter::portToString(uint16_t portAsInt) -{ - std::ostringstream stream; - stream << portAsInt; - m_Port = stream.str(); -} - -PortFilter::PortFilter(uint16_t port, Direction dir) : IFilterWithDirection(dir) -{ - portToString(port); -} - -void PortFilter::parseToString(std::string& result) -{ - std::string dir; - parseDirection(dir); - result = dir + " port " + m_Port; -} - -void PortRangeFilter::parseToString(std::string& result) -{ - std::string dir; - parseDirection(dir); - - std::ostringstream fromPortStream; - fromPortStream << (int)m_FromPort; - std::ostringstream toPortStream; - toPortStream << (int)m_ToPort; - - result = dir + " portrange " + fromPortStream.str() + '-' + toPortStream.str(); -} - -void MacAddressFilter::parseToString(std::string& result) -{ - if (getDir() != SRC_OR_DST) - { - std::string dir; - parseDirection(dir); - result = "ether " + dir + ' ' + m_MacAddress.toString(); - } - else - result = "ether host " + m_MacAddress.toString(); -} - -void EtherTypeFilter::parseToString(std::string& result) -{ - std::ostringstream stream; - stream << "0x" << std::hex << m_EtherType; - result = "ether proto " + stream.str(); -} - -AndFilter::AndFilter(std::vector& filters) -{ - for(std::vector::iterator it = filters.begin(); it != filters.end(); ++it) - { - m_FilterList.push_back(*it); - } -} - -void AndFilter::setFilters(std::vector& filters) -{ - m_FilterList.clear(); - - for(std::vector::iterator it = filters.begin(); it != filters.end(); ++it) - { - m_FilterList.push_back(*it); - } -} - -void AndFilter::parseToString(std::string& result) -{ - result.clear(); - for(std::vector::iterator it = m_FilterList.begin(); it != m_FilterList.end(); ++it) - { - std::string innerFilter; - (*it)->parseToString(innerFilter); - result += '(' + innerFilter + ')'; - if (m_FilterList.back() != *it) - { - result += " and "; - } - } -} - -OrFilter::OrFilter(std::vector& filters) -{ - for(std::vector::iterator it = filters.begin(); it != filters.end(); ++it) - { - m_FilterList.push_back(*it); - } -} - -void OrFilter::parseToString(std::string& result) -{ - result.clear(); - for(std::vector::iterator it = m_FilterList.begin(); it != m_FilterList.end(); ++it) - { - std::string innerFilter; - (*it)->parseToString(innerFilter); - result += '(' + innerFilter + ')'; - if (m_FilterList.back() != *it) - { - result += " or "; - } - } -} - -void NotFilter::parseToString(std::string& result) -{ - std::string innerFilterAsString; - m_FilterToInverse->parseToString(innerFilterAsString); - result = "not (" + innerFilterAsString + ')'; -} - -void ProtoFilter::parseToString(std::string& result) -{ - std::ostringstream stream; - - switch (m_Proto) - { - case TCP: - result = "tcp"; - break; - case UDP: - result = "udp"; - break; - case ICMP: - result = "icmp"; - break; - case VLAN: - result = "vlan"; - break; - case IPv4: - result = "ip"; - break; - case IPv6: - result = "ip6"; - break; - case ARP: - result = "arp"; - break; - case Ethernet: - result = "ether"; - break; - case GRE: - stream << "proto " << PACKETPP_IPPROTO_GRE; - result = stream.str(); - break; - case IGMP: - stream << "proto " << PACKETPP_IPPROTO_IGMP; - result = stream.str(); - break; - default: - break; - } -} - -void ArpFilter::parseToString(std::string& result) -{ - std::ostringstream sstream; - sstream << "arp[7] = " << m_OpCode; - result += sstream.str(); -} - -void VlanFilter::parseToString(std::string& result) -{ - std::ostringstream stream; - stream << m_VlanID; - result = "vlan " + stream.str(); -} - -void TcpFlagsFilter::parseToString(std::string& result) -{ - if (m_TcpFlagsBitMask == 0) - { - result.clear(); - return; - } - - result = "tcp[tcpflags] & ("; - if ((m_TcpFlagsBitMask & tcpFin) != 0) - result += "tcp-fin|"; - if ((m_TcpFlagsBitMask & tcpSyn) != 0) - result += "tcp-syn|"; - if ((m_TcpFlagsBitMask & tcpRst) != 0) - result += "tcp-rst|"; - if ((m_TcpFlagsBitMask & tcpPush) != 0) - result += "tcp-push|"; - if ((m_TcpFlagsBitMask & tcpAck) != 0) - result += "tcp-ack|"; - if ((m_TcpFlagsBitMask & tcpUrg) != 0) - result += "tcp-urg|"; - - // replace the last '|' character - result[result.size() - 1] = ')'; - - if (m_MatchOption == MatchOneAtLeast) - result += " != 0"; - else //m_MatchOption == MatchAll - { - std::ostringstream stream; - stream << (int)m_TcpFlagsBitMask; - result += " = " + stream.str(); - } -} - -void TcpWindowSizeFilter::parseToString(std::string& result) -{ - std::ostringstream stream; - stream << m_WindowSize; - result = "tcp[14:2] " + parseOperator() + ' ' + stream.str(); -} - -void UdpLengthFilter::parseToString(std::string& result) -{ - std::ostringstream stream; - stream << m_Length; - result = "udp[4:2] " + parseOperator() + ' ' + stream.str(); -} - -} // namespace pcpp +#define LOG_MODULE PcapLogModuleLiveDevice + +#include "PcapFilter.h" +#include "Logger.h" +#include "IPv4Layer.h" +#include +#if defined(_WIN32) +#include +#endif +#include "pcap.h" +#include "RawPacket.h" +#include "TimespecTimeval.h" + +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); +} + +BpfFilterWrapper::BpfFilterWrapper() +{ + m_Program = nullptr; + m_LinkType = LINKTYPE_ETHERNET; +} + +BpfFilterWrapper::~BpfFilterWrapper() +{ + freeProgram(); +} + +bool BpfFilterWrapper::setFilter(const std::string& filter, LinkLayerType linkType) +{ + if (filter.empty()) + { + freeProgram(); + return true; + } + + if (filter != m_FilterStr || linkType != m_LinkType) + { + bpf_program* newProg = new bpf_program; + if (pcap_compile_nopcap(DEFAULT_SNAPLEN, linkType, newProg, filter.c_str(), 1, 0) < 0) + { + delete newProg; + return false; + } + else + { + freeProgram(); + m_Program = newProg; + m_FilterStr = filter; + m_LinkType = linkType; + } + } + + return true; +} + +void BpfFilterWrapper::freeProgram() +{ + if (m_Program != nullptr) + { + pcap_freecode(m_Program); + delete m_Program; + m_Program = nullptr; + m_FilterStr.clear(); + } +} + +bool BpfFilterWrapper::matchPacketWithFilter(const RawPacket* rawPacket) +{ + return matchPacketWithFilter(rawPacket->getRawData(), rawPacket->getRawDataLen(), rawPacket->getPacketTimeStamp(), rawPacket->getLinkLayerType()); +} + +bool BpfFilterWrapper::matchPacketWithFilter(const uint8_t* packetData, uint32_t packetDataLength, timespec packetTimestamp, uint16_t linkType) +{ + if (m_FilterStr.empty()) + return true; + + if (!setFilter(std::string(m_FilterStr), static_cast(linkType))) + { + return false; + } + + struct pcap_pkthdr pktHdr; + pktHdr.caplen = packetDataLength; + pktHdr.len = packetDataLength; + TIMESPEC_TO_TIMEVAL(&pktHdr.ts, &packetTimestamp); + + return (pcap_offline_filter(m_Program, &pktHdr, packetData) != 0); +} + +void BPFStringFilter::parseToString(std::string& result) +{ + result = m_FilterStr; +} + +bool BPFStringFilter::verifyFilter() +{ + return m_BpfWrapper.setFilter(m_FilterStr); +} + +void IFilterWithDirection::parseDirection(std::string& directionAsString) +{ + switch (m_Dir) + { + case SRC: + directionAsString = "src"; + break; + case DST: + directionAsString = "dst"; + break; + default: //SRC_OR_DST: + directionAsString = "src or dst"; + break; + } +} + +std::string IFilterWithOperator::parseOperator() +{ + switch(m_Operator) + { + case EQUALS: + return "="; + case NOT_EQUALS: + return "!="; + case GREATER_THAN: + return ">"; + case GREATER_OR_EQUAL: + return ">="; + case LESS_THAN: + return "<"; + case LESS_OR_EQUAL: + return "<="; + default: + return ""; + } +} + +void IPFilter::convertToIPAddressWithMask(std::string& ipAddrmodified, std::string& mask) const +{ + if (m_IPv4Mask.empty()) + return; + + // Handle the mask + + // The following code lines verify both ipAddress and ipv4Mask are valid IPv4 addresses + // The IPv4 limitation comes from the fact libPcap/WinPcap/Npcap doesn't support mask for IPv6 addresses + + IPv4Address ipAddr(m_Address); + if (!ipAddr.isValid()) + { + PCPP_LOG_ERROR("IP filter with mask must be used with IPv4 valid address. Setting the mask to an empty value"); + mask.clear(); + return; + } + + IPv4Address maskAsAddr(m_IPv4Mask); + if (!maskAsAddr.isValid()) + { + PCPP_LOG_ERROR("Invalid IPv4 mask. Setting the mask to an empty"); + mask.clear(); + return; + } + + // If all addresses are IPv4 valid addresses, make sure ipAddress matches the mask. If it's not, mask the address with the mask + // The reason for doing that is libPcap/WinPcap/Npcap doesn't allow filtering an IP address that doesn't match the mask + + uint32_t addrAsIntAfterMask = ipAddr.toInt() & maskAsAddr.toInt(); + ipAddrmodified = IPv4Address(addrAsIntAfterMask).toString(); +} + +void IPFilter::convertToIPAddressWithLen(std::string& ipAddrmodified) const +{ + if (m_Len == 0) + return; + + // Handle the length + + // The following code lines verify IP address is valid (IPv4 or IPv6) + + IPAddress ipAddr = IPAddress(ipAddrmodified); + if (!ipAddr.isValid()) + { + PCPP_LOG_ERROR("Invalid IP address '" << ipAddrmodified << "', setting len to zero"); + return; + } + + if (ipAddr.getType() == IPAddress::IPv4AddressType) + { + uint32_t addrAsInt = ipAddr.getIPv4().toInt(); + uint32_t mask = ((uint32_t) - 1) >> ((sizeof(uint32_t) * 8) - m_Len); + addrAsInt &= mask; + ipAddrmodified = IPv4Address(addrAsInt).toString(); + } +} + +void IPFilter::parseToString(std::string& result) +{ + std::string dir; + std::string ipAddr = m_Address; + std::string mask = m_IPv4Mask; + convertToIPAddressWithMask(ipAddr, mask); + convertToIPAddressWithLen(ipAddr); + parseDirection(dir); + result = "ip and " + dir + " net " + ipAddr; + if (m_IPv4Mask != "") + result += " mask " + mask; + else if (m_Len > 0) + { + std::ostringstream stream; + stream << m_Len; + result += '/' + stream.str(); + } +} + +void IPv4IDFilter::parseToString(std::string& result) +{ + std::string op = parseOperator(); + std::ostringstream stream; + stream << m_IpID; + result = "ip[4:2] " + op + ' ' + stream.str(); +} + +void IPv4TotalLengthFilter::parseToString(std::string& result) +{ + std::string op = parseOperator(); + std::ostringstream stream; + stream << m_TotalLength; + result = "ip[2:2] " + op + ' ' + stream.str(); +} + +void PortFilter::portToString(uint16_t portAsInt) +{ + std::ostringstream stream; + stream << portAsInt; + m_Port = stream.str(); +} + +PortFilter::PortFilter(uint16_t port, Direction dir) : IFilterWithDirection(dir) +{ + portToString(port); +} + +void PortFilter::parseToString(std::string& result) +{ + std::string dir; + parseDirection(dir); + result = dir + " port " + m_Port; +} + +void PortRangeFilter::parseToString(std::string& result) +{ + std::string dir; + parseDirection(dir); + + std::ostringstream fromPortStream; + fromPortStream << (int)m_FromPort; + std::ostringstream toPortStream; + toPortStream << (int)m_ToPort; + + result = dir + " portrange " + fromPortStream.str() + '-' + toPortStream.str(); +} + +void MacAddressFilter::parseToString(std::string& result) +{ + if (getDir() != SRC_OR_DST) + { + std::string dir; + parseDirection(dir); + result = "ether " + dir + ' ' + m_MacAddress.toString(); + } + else + result = "ether host " + m_MacAddress.toString(); +} + +void EtherTypeFilter::parseToString(std::string& result) +{ + std::ostringstream stream; + stream << "0x" << std::hex << m_EtherType; + result = "ether proto " + stream.str(); +} + +AndFilter::AndFilter(std::vector& filters) +{ + for(std::vector::iterator it = filters.begin(); it != filters.end(); ++it) + { + m_FilterList.push_back(*it); + } +} + +void AndFilter::setFilters(std::vector& filters) +{ + m_FilterList.clear(); + + for(std::vector::iterator it = filters.begin(); it != filters.end(); ++it) + { + m_FilterList.push_back(*it); + } +} + +void AndFilter::parseToString(std::string& result) +{ + result.clear(); + for(std::vector::iterator it = m_FilterList.begin(); it != m_FilterList.end(); ++it) + { + std::string innerFilter; + (*it)->parseToString(innerFilter); + result += '(' + innerFilter + ')'; + if (m_FilterList.back() != *it) + { + result += " and "; + } + } +} + +OrFilter::OrFilter(std::vector& filters) +{ + for(std::vector::iterator it = filters.begin(); it != filters.end(); ++it) + { + m_FilterList.push_back(*it); + } +} + +void OrFilter::parseToString(std::string& result) +{ + result.clear(); + for(std::vector::iterator it = m_FilterList.begin(); it != m_FilterList.end(); ++it) + { + std::string innerFilter; + (*it)->parseToString(innerFilter); + result += '(' + innerFilter + ')'; + if (m_FilterList.back() != *it) + { + result += " or "; + } + } +} + +void NotFilter::parseToString(std::string& result) +{ + std::string innerFilterAsString; + m_FilterToInverse->parseToString(innerFilterAsString); + result = "not (" + innerFilterAsString + ')'; +} + +void ProtoFilter::parseToString(std::string& result) +{ + std::ostringstream stream; + + switch (m_Proto) + { + case TCP: + result = "tcp"; + break; + case UDP: + result = "udp"; + break; + case ICMP: + result = "icmp"; + break; + case VLAN: + result = "vlan"; + break; + case IPv4: + result = "ip"; + break; + case IPv6: + result = "ip6"; + break; + case ARP: + result = "arp"; + break; + case Ethernet: + result = "ether"; + break; + case GRE: + stream << "proto " << PACKETPP_IPPROTO_GRE; + result = stream.str(); + break; + case IGMP: + stream << "proto " << PACKETPP_IPPROTO_IGMP; + result = stream.str(); + break; + default: + break; + } +} + +void ArpFilter::parseToString(std::string& result) +{ + std::ostringstream sstream; + sstream << "arp[7] = " << m_OpCode; + result += sstream.str(); +} + +void VlanFilter::parseToString(std::string& result) +{ + std::ostringstream stream; + stream << m_VlanID; + result = "vlan " + stream.str(); +} + +void TcpFlagsFilter::parseToString(std::string& result) +{ + if (m_TcpFlagsBitMask == 0) + { + result.clear(); + return; + } + + result = "tcp[tcpflags] & ("; + if ((m_TcpFlagsBitMask & tcpFin) != 0) + result += "tcp-fin|"; + if ((m_TcpFlagsBitMask & tcpSyn) != 0) + result += "tcp-syn|"; + if ((m_TcpFlagsBitMask & tcpRst) != 0) + result += "tcp-rst|"; + if ((m_TcpFlagsBitMask & tcpPush) != 0) + result += "tcp-push|"; + if ((m_TcpFlagsBitMask & tcpAck) != 0) + result += "tcp-ack|"; + if ((m_TcpFlagsBitMask & tcpUrg) != 0) + result += "tcp-urg|"; + + // replace the last '|' character + result[result.size() - 1] = ')'; + + if (m_MatchOption == MatchOneAtLeast) + result += " != 0"; + else //m_MatchOption == MatchAll + { + std::ostringstream stream; + stream << (int)m_TcpFlagsBitMask; + result += " = " + stream.str(); + } +} + +void TcpWindowSizeFilter::parseToString(std::string& result) +{ + std::ostringstream stream; + stream << m_WindowSize; + result = "tcp[14:2] " + parseOperator() + ' ' + stream.str(); +} + +void UdpLengthFilter::parseToString(std::string& result) +{ + std::ostringstream stream; + stream << m_Length; + result = "udp[4:2] " + parseOperator() + ' ' + stream.str(); +} + +} // namespace pcpp diff --git a/Pcap++/src/PcapLiveDevice.cpp b/Pcap++/src/PcapLiveDevice.cpp index 9526e0fba1..99c6b50ac9 100644 --- a/Pcap++/src/PcapLiveDevice.cpp +++ b/Pcap++/src/PcapLiveDevice.cpp @@ -1,980 +1,980 @@ -#define LOG_MODULE PcapLogModuleLiveDevice - -#include "IpUtils.h" -#include "PcapLiveDevice.h" -#include "PcapLiveDeviceList.h" -#include "Packet.h" -#ifndef _MSC_VER -#include -#endif // ! _MSC_VER -#include "pcap.h" -#include -#include "Logger.h" -#include "SystemUtils.h" -#include -#include -#include -#include -#if defined(_WIN32) -// The definition of BPF_MAJOR_VERSION is required to support Npcap. In Npcap there are -// compilation errors due to struct redefinition when including both Packet32.h and pcap.h -// This define statement eliminates these errors -#ifndef BPF_MAJOR_VERSION -#define BPF_MAJOR_VERSION 1 -#endif // BPF_MAJOR_VERSION -#include -#include -#include -#include -#else -#include -#include -#include -#endif // if defined(_WIN32) -#if defined(__APPLE__) || defined(__FreeBSD__) -#include -#include -#endif - -// On Mac OS X and FreeBSD timeout of -1 causes pcap_open_live to fail so value of 1ms is set here. -// On Linux and Windows this is not the case so we keep the -1 value -#if defined(__APPLE__) || defined(__FreeBSD__) -#define LIBPCAP_OPEN_LIVE_TIMEOUT 1 -#else -#define LIBPCAP_OPEN_LIVE_TIMEOUT -1 -#endif - -static const int DEFAULT_SNAPLEN = 9000; - -namespace pcpp -{ - -#ifdef HAS_SET_DIRECTION_ENABLED -static pcap_direction_t directionTypeMap(PcapLiveDevice::PcapDirection direction) -{ - switch (direction) - { - case PcapLiveDevice::PCPP_IN: return PCAP_D_IN; - case PcapLiveDevice::PCPP_OUT: return PCAP_D_OUT; - case PcapLiveDevice::PCPP_INOUT: return PCAP_D_INOUT; - } -} -#endif - - - -PcapLiveDevice::PcapLiveDevice(pcap_if_t* pInterface, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway) : IPcapDevice(), - m_MacAddress(""), m_DefaultGateway(IPv4Address::Zero) -{ - m_DeviceMtu = 0; - m_LinkType = LINKTYPE_ETHERNET; - - m_IsLoopback = (pInterface->flags & 0x1) == PCAP_IF_LOOPBACK; - - m_Name = pInterface->name; - if (pInterface->description != nullptr) - m_Description = pInterface->description; - PCPP_LOG_DEBUG("Added live device: name=" << m_Name << "; desc=" << m_Description); - PCPP_LOG_DEBUG(" Addresses:"); - while (pInterface->addresses != nullptr) - { - m_Addresses.insert(m_Addresses.end(), *(pInterface->addresses)); - pInterface->addresses = pInterface->addresses->next; - if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && pInterface->addresses != nullptr && pInterface->addresses->addr != nullptr) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(pInterface->addresses->addr, addrAsString); - PCPP_LOG_DEBUG(" " << addrAsString); - } - } - - if (calculateMTU) - { - setDeviceMtu(); - PCPP_LOG_DEBUG(" MTU: " << m_DeviceMtu); - } - - if (calculateDefaultGateway) - { - setDefaultGateway(); - PCPP_LOG_DEBUG(" Default Gateway: " << m_DefaultGateway); - } - - //init all other members - m_CaptureThreadStarted = false; - m_StatsThreadStarted = false; - m_IsLoopback = false; - m_StopThread = false; - m_CaptureThread = {}; - m_StatsThread = {}; - m_cbOnPacketArrives = nullptr; - m_cbOnStatsUpdate = nullptr; - m_cbOnPacketArrivesBlockingMode = nullptr; - m_cbOnPacketArrivesBlockingModeUserCookie = nullptr; - m_IntervalToUpdateStats = 0; - m_cbOnPacketArrivesUserCookie = nullptr; - m_cbOnStatsUpdateUserCookie = nullptr; - m_CaptureCallbackMode = true; - m_CapturedPackets = nullptr; - if (calculateMacAddress) - { - setDeviceMacAddress(); - if (m_MacAddress.isValid()) - PCPP_LOG_DEBUG(" MAC addr: " << m_MacAddress); - } -} - -void PcapLiveDevice::onPacketArrives(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet) -{ - PcapLiveDevice* pThis = (PcapLiveDevice*)user; - if (pThis == nullptr) - { - PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); - return; - } - - RawPacket rawPacket(packet, pkthdr->caplen, pkthdr->ts, false, pThis->getLinkType()); - - if (pThis->m_cbOnPacketArrives != nullptr) - pThis->m_cbOnPacketArrives(&rawPacket, pThis, pThis->m_cbOnPacketArrivesUserCookie); -} - -void PcapLiveDevice::onPacketArrivesNoCallback(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet) -{ - PcapLiveDevice* pThis = (PcapLiveDevice*)user; - if (pThis == nullptr) - { - PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); - return; - } - - uint8_t* packetData = new uint8_t[pkthdr->caplen]; - memcpy(packetData, packet, pkthdr->caplen); - RawPacket* rawPacketPtr = new RawPacket(packetData, pkthdr->caplen, pkthdr->ts, true, pThis->getLinkType()); - pThis->m_CapturedPackets->pushBack(rawPacketPtr); -} - -void PcapLiveDevice::onPacketArrivesBlockingMode(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet) -{ - PcapLiveDevice* pThis = (PcapLiveDevice*)user; - if (pThis == nullptr) - { - PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); - return; - } - - RawPacket rawPacket(packet, pkthdr->caplen, pkthdr->ts, false, pThis->getLinkType()); - - if (pThis->m_cbOnPacketArrivesBlockingMode != nullptr) - if (pThis->m_cbOnPacketArrivesBlockingMode(&rawPacket, pThis, pThis->m_cbOnPacketArrivesBlockingModeUserCookie)) - pThis->m_StopThread = true; -} - -void PcapLiveDevice::captureThreadMain() -{ - PCPP_LOG_DEBUG("Started capture thread for device '" << m_Name << "'"); - if (m_CaptureCallbackMode) - { - while (!m_StopThread) - pcap_dispatch(m_PcapDescriptor, -1, onPacketArrives, (uint8_t*)this); - } - else - { - while (!m_StopThread) - pcap_dispatch(m_PcapDescriptor, 100, onPacketArrivesNoCallback, (uint8_t*)this); - } - PCPP_LOG_DEBUG("Ended capture thread for device '" << m_Name << "'"); -} - -void PcapLiveDevice::statsThreadMain() -{ - PCPP_LOG_DEBUG("Started stats thread for device '" << m_Name << "'"); - while (!m_StopThread) - { - PcapStats stats; - getStatistics(stats); - m_cbOnStatsUpdate(stats, m_cbOnStatsUpdateUserCookie); - multiPlatformSleep(m_IntervalToUpdateStats); - } - PCPP_LOG_DEBUG("Ended stats thread for device '" << m_Name << "'"); -} - -pcap_t* PcapLiveDevice::doOpen(const DeviceConfiguration& config) -{ - char errbuf[PCAP_ERRBUF_SIZE] = {'\0'}; - pcap_t* pcap = pcap_create(m_Name.c_str(), errbuf); - if (!pcap) - { - PCPP_LOG_ERROR(errbuf); - return pcap; - } - int ret = pcap_set_snaplen(pcap, config.snapshotLength <= 0 ? DEFAULT_SNAPLEN : config.snapshotLength); - if (ret != 0) - { - PCPP_LOG_ERROR(pcap_geterr(pcap)); - } - ret = pcap_set_promisc(pcap, config.mode); - if (ret != 0) - { - PCPP_LOG_ERROR(pcap_geterr(pcap)); - } - - int timeout = (config.packetBufferTimeoutMs <= 0 ? LIBPCAP_OPEN_LIVE_TIMEOUT : config.packetBufferTimeoutMs); - ret = pcap_set_timeout(pcap, timeout); - if (ret != 0) - { - PCPP_LOG_ERROR(pcap_geterr(pcap)); - } - - if (config.packetBufferSize >= 100) - { - ret = pcap_set_buffer_size(pcap, config.packetBufferSize); - if (ret != 0) - { - PCPP_LOG_ERROR(pcap_geterr(pcap)); - } - } - -#ifdef HAS_PCAP_IMMEDIATE_MODE - ret = pcap_set_immediate_mode(pcap, 1); - if (ret == 0) - { - PCPP_LOG_DEBUG("Immediate mode is activated"); - } - else - { - PCPP_LOG_ERROR("Failed to activate immediate mode, error code: '" << ret << "', error message: '" << pcap_geterr(pcap) << "'"); - } -#endif - - ret = pcap_activate(pcap); - if (ret != 0) - { - PCPP_LOG_ERROR(pcap_geterr(pcap)); - pcap_close(pcap); - return nullptr; - } - -#ifdef HAS_SET_DIRECTION_ENABLED - pcap_direction_t directionToSet = directionTypeMap(config.direction); - ret = pcap_setdirection(pcap, directionToSet); - if (ret == 0) - { - if (config.direction == PCPP_IN) - { - PCPP_LOG_DEBUG("Only incoming traffics will be captured"); - } - else if (config.direction == PCPP_OUT) - { - PCPP_LOG_DEBUG("Only outgoing traffics will be captured"); - } - else - { - PCPP_LOG_DEBUG("Both incoming and outgoing traffics will be captured"); - } - } - else - { - PCPP_LOG_ERROR("Failed to set direction for capturing packets, error code: '" << ret << "', error message: '" << pcap_geterr(pcap) << "'"); - } -#endif - - if (pcap) - { - int dlt = pcap_datalink(pcap); - const char* dlt_name = pcap_datalink_val_to_name(dlt); - if (dlt_name) - { - PCPP_LOG_DEBUG("link-type " << dlt << ": " << dlt_name << " (" << pcap_datalink_val_to_description(dlt) << ")"); - } - else - { - PCPP_LOG_DEBUG("link-type " << dlt); - } - - m_LinkType = static_cast(dlt); - } - return pcap; -} - -bool PcapLiveDevice::open(const DeviceConfiguration& config) -{ - if (m_DeviceOpened) - { - PCPP_LOG_DEBUG("Device '" << m_Name << "' already opened"); - return true; - } - - m_PcapDescriptor = doOpen(config); - m_PcapSendDescriptor = doOpen(config); - if (m_PcapDescriptor == nullptr || m_PcapSendDescriptor == nullptr) - { - m_DeviceOpened = false; - return false; - } - - PCPP_LOG_DEBUG("Device '" << m_Name << "' opened"); - - m_DeviceOpened = true; - - return true; -} - -bool PcapLiveDevice::open() -{ - DeviceConfiguration defaultConfig; - return open(defaultConfig); -} - -void PcapLiveDevice::close() -{ - if (m_PcapDescriptor == nullptr && m_PcapSendDescriptor == nullptr) - { - PCPP_LOG_DEBUG("Device '" << m_Name << "' already closed"); - return; - } - - bool sameDescriptor = (m_PcapDescriptor == m_PcapSendDescriptor); - pcap_close(m_PcapDescriptor); - PCPP_LOG_DEBUG("Receive pcap descriptor closed"); - if (!sameDescriptor) - { - pcap_close(m_PcapSendDescriptor); - PCPP_LOG_DEBUG("Send pcap descriptor closed"); - } - - m_DeviceOpened = false; - PCPP_LOG_DEBUG("Device '" << m_Name << "' closed"); -} - -PcapLiveDevice* PcapLiveDevice::clone() -{ - PcapLiveDevice *retval = nullptr; - - pcap_if_t *interfaceList; - char errbuf[PCAP_ERRBUF_SIZE]; - int err = pcap_findalldevs(&interfaceList, errbuf); - if (err < 0) - { - PCPP_LOG_ERROR("Error searching for devices: " << errbuf); - return nullptr; - } - - pcap_if_t* currInterface = interfaceList; - while (currInterface != nullptr) - { - if(!strcmp(currInterface->name, getName().c_str())) - break; - currInterface = currInterface->next; - } - - if(currInterface) - retval = new PcapLiveDevice(currInterface, true, true, true); - else - PCPP_LOG_ERROR("Can't find interface " << getName().c_str()); - - pcap_freealldevs(interfaceList); - return retval; -} - -bool PcapLiveDevice::startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie) -{ - return startCapture(onPacketArrives, onPacketArrivesUserCookie, 0, nullptr, nullptr); -} - -bool PcapLiveDevice::startCapture(int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie) -{ - return startCapture(nullptr, nullptr, intervalInSecondsToUpdateStats, onStatsUpdate, onStatsUpdateUserCookie); -} - -bool PcapLiveDevice::startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie, int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie) -{ - if (!m_DeviceOpened || m_PcapDescriptor == nullptr) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); - return false; - } - - if (m_CaptureThreadStarted) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' already capturing traffic"); - return false; - } - - m_IntervalToUpdateStats = intervalInSecondsToUpdateStats; - - m_CaptureCallbackMode = true; - m_cbOnPacketArrives = onPacketArrives; - m_cbOnPacketArrivesUserCookie = onPacketArrivesUserCookie; - - m_CaptureThread = std::thread(&pcpp::PcapLiveDevice::captureThreadMain, this); - m_CaptureThreadStarted = true; - PCPP_LOG_DEBUG("Successfully created capture thread for device '" << m_Name << "'. Thread id: " << m_CaptureThread.get_id()); - - if (onStatsUpdate != nullptr && intervalInSecondsToUpdateStats > 0) - { - m_cbOnStatsUpdate = onStatsUpdate; - m_cbOnStatsUpdateUserCookie = onStatsUpdateUserCookie; - m_StatsThread = std::thread(&pcpp::PcapLiveDevice::statsThreadMain, this); - m_StatsThreadStarted = true; - PCPP_LOG_DEBUG("Successfully created stats thread for device '" << m_Name << "'. Thread id: " << m_StatsThread.get_id()); - } - - return true; -} - -bool PcapLiveDevice::startCapture(RawPacketVector& capturedPacketsVector) -{ - if (!m_DeviceOpened || m_PcapDescriptor == nullptr) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); - return false; - } - - if (m_CaptureThreadStarted) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' already capturing traffic"); - return false; - } - - m_CapturedPackets = &capturedPacketsVector; - m_CapturedPackets->clear(); - - m_CaptureCallbackMode = false; - m_CaptureThread = std::thread(&pcpp::PcapLiveDevice::captureThreadMain, this); - m_CaptureThreadStarted = true; - PCPP_LOG_DEBUG("Successfully created capture thread for device '" << m_Name << "'. Thread id: " << m_CaptureThread.get_id()); - - return true; -} - - -int PcapLiveDevice::startCaptureBlockingMode(OnPacketArrivesStopBlocking onPacketArrives, void* userCookie, int timeout) -{ - if (!m_DeviceOpened || m_PcapDescriptor == nullptr) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); - return 0; - } - - if (m_CaptureThreadStarted) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' already capturing traffic"); - return 0; - } - - m_cbOnPacketArrives = nullptr; - m_cbOnStatsUpdate = nullptr; - m_cbOnPacketArrivesUserCookie = nullptr; - m_cbOnStatsUpdateUserCookie = nullptr; - - m_cbOnPacketArrivesBlockingMode = onPacketArrives; - m_cbOnPacketArrivesBlockingModeUserCookie = userCookie; - - long startTimeSec = 0, startTimeNSec = 0; - clockGetTime(startTimeSec, startTimeNSec); - - long curTimeSec = 0; - - m_CaptureThreadStarted = true; - m_StopThread = false; - - if (timeout <= 0) - { - while (!m_StopThread) - { - pcap_dispatch(m_PcapDescriptor, -1, onPacketArrivesBlockingMode, (uint8_t*)this); - } - curTimeSec = startTimeSec + timeout; - } - else - { - while (!m_StopThread && curTimeSec <= (startTimeSec + timeout)) - { - long curTimeNSec = 0; - pcap_dispatch(m_PcapDescriptor, -1, onPacketArrivesBlockingMode, (uint8_t*)this); - clockGetTime(curTimeSec, curTimeNSec); - } - } - - m_CaptureThreadStarted = false; - - m_StopThread = false; - - m_cbOnPacketArrivesBlockingMode = nullptr; - m_cbOnPacketArrivesBlockingModeUserCookie = nullptr; - - if (curTimeSec > (startTimeSec + timeout)) - return -1; - return 1; -} - -void PcapLiveDevice::stopCapture() -{ - // in blocking mode stop capture isn't relevant - if (m_cbOnPacketArrivesBlockingMode != nullptr) - return; - - m_StopThread = true; - if (m_CaptureThreadStarted) - { - pcap_breakloop(m_PcapDescriptor); - PCPP_LOG_DEBUG("Stopping capture thread, waiting for it to join..."); - m_CaptureThread.join(); - m_CaptureThreadStarted = false; - PCPP_LOG_DEBUG("Capture thread stopped for device '" << m_Name << "'"); - } - PCPP_LOG_DEBUG("Capture thread stopped for device '" << m_Name << "'"); - if (m_StatsThreadStarted) - { - PCPP_LOG_DEBUG("Stopping stats thread, waiting for it to join..."); - m_StatsThread.join(); - m_StatsThreadStarted = false; - PCPP_LOG_DEBUG("Stats thread stopped for device '" << m_Name << "'"); - } - - multiPlatformSleep(1); - m_StopThread = false; -} - -bool PcapLiveDevice::captureActive() -{ - return m_CaptureThreadStarted; -} - -void PcapLiveDevice::getStatistics(PcapStats& stats) const -{ - pcap_stat pcapStats; - if (pcap_stats(m_PcapDescriptor, &pcapStats) < 0) - { - PCPP_LOG_ERROR("Error getting statistics from live device '" << m_Name << "'"); - } - - stats.packetsRecv = pcapStats.ps_recv; - stats.packetsDrop = pcapStats.ps_drop; - stats.packetsDropByInterface = pcapStats.ps_ifdrop; -} - -bool PcapLiveDevice::doMtuCheck(int packetPayloadLength) -{ - if (packetPayloadLength > (int)m_DeviceMtu) - { - PCPP_LOG_ERROR("Payload length [" << packetPayloadLength << "] is larger than device MTU [" << m_DeviceMtu << "]"); - return false; - } - return true; -} - -bool PcapLiveDevice::sendPacket(RawPacket const& rawPacket, bool checkMtu) -{ - if (checkMtu) - { - RawPacket *rPacket = (RawPacket *)&rawPacket; - Packet parsedPacket = Packet(rPacket, OsiModelDataLinkLayer); - return sendPacket(&parsedPacket, true); - } - // Send packet without Mtu check - return sendPacket(((RawPacket&)rawPacket).getRawData(), ((RawPacket&)rawPacket).getRawDataLen()); -} - -bool PcapLiveDevice::sendPacket(const uint8_t* packetData, int packetDataLength, int packetPayloadLength) -{ - return doMtuCheck(packetPayloadLength) && sendPacket(packetData, packetDataLength); -} - -bool PcapLiveDevice::sendPacket(const uint8_t* packetData, int packetDataLength, bool checkMtu, pcpp::LinkLayerType linkType) -{ - if (checkMtu) - { - timeval time; - gettimeofday(&time, nullptr); - pcpp::RawPacket rawPacket(packetData, packetDataLength, time, false, linkType); - Packet parsedPacket = Packet(&rawPacket, pcpp::OsiModelDataLinkLayer); - return sendPacket(&parsedPacket, true); - } - - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened!"); - return false; - } - - if (packetDataLength == 0) - { - PCPP_LOG_ERROR("Trying to send a packet with length 0"); - return false; - } - - if (pcap_sendpacket(m_PcapSendDescriptor, packetData, packetDataLength) == -1) - { - PCPP_LOG_ERROR("Error sending packet: " << pcap_geterr(m_PcapSendDescriptor)); - return false; - } - - PCPP_LOG_DEBUG("Packet sent successfully. Packet length: " << packetDataLength); - return true; -} - -bool PcapLiveDevice::sendPacket(Packet* packet, bool checkMtu) -{ - RawPacket* rawPacket = packet->getRawPacket(); - if (checkMtu) - { - int packetPayloadLength = 0; - switch (packet->getFirstLayer()->getOsiModelLayer()) - { - case (pcpp::OsiModelDataLinkLayer): - packetPayloadLength = (int)packet->getFirstLayer()->getLayerPayloadSize(); - break; - case (pcpp::OsiModelNetworkLayer): - packetPayloadLength = (int)packet->getFirstLayer()->getDataLen(); - break; - default: - // if packet layer is not known, do not perform MTU check. - return sendPacket(*rawPacket, false); - } - return doMtuCheck(packetPayloadLength) && sendPacket(*rawPacket, false); - } - return sendPacket(*rawPacket, false); -} - -int PcapLiveDevice::sendPackets(RawPacket* rawPacketsArr, int arrLength, bool checkMtu) -{ - int packetsSent = 0; - for (int i = 0; i < arrLength; i++) - { - if (sendPacket(rawPacketsArr[i], checkMtu)) - packetsSent++; - } - - PCPP_LOG_DEBUG(packetsSent << " packets sent successfully. " << arrLength-packetsSent << " packets not sent"); - return packetsSent; -} - -int PcapLiveDevice::sendPackets(Packet** packetsArr, int arrLength, bool checkMtu) -{ - int packetsSent = 0; - for (int i = 0; i < arrLength; i++) - { - if (sendPacket(packetsArr[i], checkMtu)) - packetsSent++; - } - - PCPP_LOG_DEBUG(packetsSent << " packets sent successfully. " << arrLength-packetsSent << " packets not sent"); - return packetsSent; -} - -int PcapLiveDevice::sendPackets(const RawPacketVector& rawPackets, bool checkMtu) -{ - int packetsSent = 0; - for (RawPacketVector::ConstVectorIterator iter = rawPackets.begin(); iter != rawPackets.end(); iter++) - { - if (sendPacket(**iter, checkMtu)) - packetsSent++; - } - - PCPP_LOG_DEBUG(packetsSent << " packets sent successfully. " << (rawPackets.size()-packetsSent) << " packets not sent"); - return packetsSent; -} - -void PcapLiveDevice::setDeviceMtu() -{ -#if defined(_WIN32) - - if (m_IsLoopback) - { - PCPP_LOG_DEBUG("Npcap Loopback Adapter - MTU is insignificant, setting MTU to max value (0xffffffff)"); - m_DeviceMtu = 0xffffffff; - return; - } - - uint32_t mtuValue = 0; - LPADAPTER adapter = PacketOpenAdapter((char*)m_Name.c_str()); - if (adapter == NULL) - { - PCPP_LOG_ERROR("Error in retrieving MTU: Adapter is NULL"); - return; - } - - uint8_t buffer[512]; - PACKET_OID_DATA* oidData = (PACKET_OID_DATA*)buffer; - oidData->Oid = OID_GEN_MAXIMUM_TOTAL_SIZE; - oidData->Length = sizeof(uint32_t); - memcpy(oidData->Data, &mtuValue, sizeof(uint32_t)); - if (PacketRequest(adapter, false, oidData)) - { - if (oidData->Length <= sizeof(uint32_t)) - { - /* copy value from driver */ - memcpy(&mtuValue, oidData->Data, oidData->Length); - // Sometimes the query gives a wrong number that includes the link header size - // A very common value is 1514 - if identify this value just reduce to 1500. - // TODO: think of a better way to always get the right value - if (mtuValue == 1514) - { - mtuValue = 1500; - } - m_DeviceMtu = mtuValue; - } - else - { - /* the driver returned a value that is longer than expected (and longer than the given buffer) */ - PCPP_LOG_ERROR("Error in retrieving MTU: Size of Oid larger than uint32_t, OidLen: " << oidData->Length); - return; - } - } - else - { - PCPP_LOG_ERROR("Error in retrieving MTU: PacketRequest failed"); - } - -#else - struct ifreq ifr; - - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, m_Name.c_str(), sizeof(ifr.ifr_name) - 1); - - int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (ioctl(socketfd, SIOCGIFMTU, &ifr) == -1) - { - PCPP_LOG_DEBUG("Error in retrieving MTU: ioctl() returned -1"); - m_DeviceMtu = 0; - return; - } - - m_DeviceMtu = ifr.ifr_mtu; -#endif -} - -void PcapLiveDevice::setDeviceMacAddress() -{ -#if defined(_WIN32) - - LPADAPTER adapter = PacketOpenAdapter((char*)m_Name.c_str()); - if (adapter == NULL) - { - PCPP_LOG_ERROR("Error in retrieving MAC address: Adapter is NULL"); - return; - } - - uint8_t buffer[512]; - PACKET_OID_DATA* oidData = (PACKET_OID_DATA*)buffer; - oidData->Oid = OID_802_3_CURRENT_ADDRESS; - oidData->Length = 6; - oidData->Data[0] = 0; - if (PacketRequest(adapter, false, oidData)) - { - if (oidData->Length == 6) - { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" - /* copy value from driver */ - m_MacAddress = MacAddress(oidData->Data[0], oidData->Data[1], oidData->Data[2], oidData->Data[3], oidData->Data[4], oidData->Data[5]); -#pragma GCC diagnostic pop - PCPP_LOG_DEBUG(" MAC address: " << m_MacAddress); - } - else - { - /* the driver returned a value that is longer than expected (and longer than the given buffer) */ - PCPP_LOG_DEBUG("Error in retrieving MAC address: Size of Oid larger than 6, OidLen: " << oidData->Length); - return; - } - } - else - { - PCPP_LOG_DEBUG("Error in retrieving MAC address: PacketRequest failed"); - } -#elif defined(__linux__) - struct ifreq ifr; - - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, m_Name.c_str(), sizeof(ifr.ifr_name) - 1); - - int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (ioctl(socketfd, SIOCGIFHWADDR, &ifr) == -1) - { - PCPP_LOG_DEBUG("Error in retrieving MAC address: ioctl() returned -1"); - return; - } - - m_MacAddress = MacAddress(ifr.ifr_hwaddr.sa_data[0], ifr.ifr_hwaddr.sa_data[1], ifr.ifr_hwaddr.sa_data[2], ifr.ifr_hwaddr.sa_data[3], ifr.ifr_hwaddr.sa_data[4], ifr.ifr_hwaddr.sa_data[5]); -#elif defined(__APPLE__) || defined(__FreeBSD__) - int mib[6]; - size_t len; - - mib[0] = CTL_NET; - mib[1] = AF_ROUTE; - mib[2] = 0; - mib[3] = AF_LINK; - mib[4] = NET_RT_IFLIST; - mib[5] = if_nametoindex(m_Name.c_str()); - - if (mib[5] == 0){ - PCPP_LOG_DEBUG("Error in retrieving MAC address: if_nametoindex error"); - return; - } - - if (sysctl(mib, 6, nullptr, &len, nullptr, 0) < 0) - { - PCPP_LOG_DEBUG("Error in retrieving MAC address: sysctl 1 error"); - return; - } - - uint8_t buf[len]; - - if (sysctl(mib, 6, buf, &len, nullptr, 0) < 0) - { - PCPP_LOG_DEBUG("Error in retrieving MAC address: sysctl 2 error"); - return; - } - - struct if_msghdr*ifm = (struct if_msghdr *)buf; - struct sockaddr_dl* sdl = (struct sockaddr_dl *)(ifm + 1); - uint8_t* ptr = (uint8_t*)LLADDR(sdl); - m_MacAddress = MacAddress(ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); -#endif -} - -void PcapLiveDevice::setDefaultGateway() -{ -#if defined(_WIN32) - ULONG outBufLen = sizeof (IP_ADAPTER_INFO); - uint8_t* buffer = new uint8_t[outBufLen]; - PIP_ADAPTER_INFO adapterInfo = (IP_ADAPTER_INFO*)buffer; - DWORD retVal = 0; - - retVal = GetAdaptersInfo(adapterInfo, &outBufLen); - uint8_t* buffer2 = new uint8_t[outBufLen]; - if (retVal == ERROR_BUFFER_OVERFLOW) - adapterInfo = (IP_ADAPTER_INFO *)buffer2; - - retVal = GetAdaptersInfo(adapterInfo, &outBufLen); - - if (retVal == NO_ERROR) - { - PIP_ADAPTER_INFO curAdapterInfo = adapterInfo; - while (curAdapterInfo != NULL) - { - if (m_Name.find(curAdapterInfo->AdapterName) != std::string::npos) - m_DefaultGateway = IPv4Address(curAdapterInfo->GatewayList.IpAddress.String); - - curAdapterInfo = curAdapterInfo->Next; - } - } - else - { - PCPP_LOG_ERROR("Error retrieving default gateway address"); - } - - delete[] buffer; - // cppcheck-suppress uninitdata - delete[] buffer2; -#elif defined(__linux__) - std::ifstream routeFile("/proc/net/route"); - std::string line; - while (std::getline(routeFile, line)) - { - std::stringstream lineStream(line); - std::string interfaceName; - std::getline(lineStream, interfaceName, '\t'); - if (interfaceName != m_Name) - continue; - - std::string interfaceDest; - std::getline(lineStream, interfaceDest, '\t'); - if (interfaceDest != "00000000") - continue; - - std::string interfaceGateway; - std::getline(lineStream, interfaceGateway, '\t'); - - uint32_t interfaceGatewayIPInt; - std::stringstream interfaceGatewayStream; - interfaceGatewayStream << std::hex << interfaceGateway; - interfaceGatewayStream >> interfaceGatewayIPInt; - m_DefaultGateway = IPv4Address(interfaceGatewayIPInt); - } -#elif defined(__APPLE__) || defined(__FreeBSD__) - std::string command = "netstat -nr | grep default | grep " + m_Name; - std::string ifaceInfo = executeShellCommand(command); - if (ifaceInfo == "") - { - PCPP_LOG_DEBUG("Error retrieving default gateway address: couldn't get netstat output"); - return; - } - - // remove the word "default" - ifaceInfo.erase(0, 7); - - // remove spaces - while (ifaceInfo.at(0) == ' ') - ifaceInfo.erase(0,1); - - // erase string after gateway IP address - ifaceInfo.resize(ifaceInfo.find(' ', 0)); - - m_DefaultGateway = IPv4Address(ifaceInfo); -#endif -} - -IPv4Address PcapLiveDevice::getIPv4Address() const -{ - for(std::vector::const_iterator addrIter = m_Addresses.begin(); addrIter != m_Addresses.end(); addrIter++) - { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter->addr != nullptr) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter->addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); - } - - in_addr* currAddr = internal::sockaddr2in_addr(addrIter->addr); - if (currAddr == nullptr) - { - PCPP_LOG_DEBUG("Address is NULL"); - continue; - } - - return IPv4Address(currAddr->s_addr); - } - - return IPv4Address::Zero; -} - -IPv6Address PcapLiveDevice::getIPv6Address() const -{ - for (std::vector::const_iterator addrIter = m_Addresses.begin(); addrIter != m_Addresses.end(); - addrIter++) - { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter->addr != nullptr) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter->addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); - } - in6_addr *currAddr = internal::sockaddr2in6_addr(addrIter->addr); - if (currAddr == nullptr) - { - PCPP_LOG_DEBUG("Address is NULL"); - continue; - } - return IPv6Address(currAddr->s6_addr); - } - return IPv6Address::Zero; -} - -IPv4Address PcapLiveDevice::getDefaultGateway() const -{ - return m_DefaultGateway; -} - -const std::vector& PcapLiveDevice::getDnsServers() const -{ - return PcapLiveDeviceList::getInstance().getDnsServers(); -} - -PcapLiveDevice::~PcapLiveDevice() -{ -} - -} // namespace pcpp +#define LOG_MODULE PcapLogModuleLiveDevice + +#include "IpUtils.h" +#include "PcapLiveDevice.h" +#include "PcapLiveDeviceList.h" +#include "Packet.h" +#ifndef _MSC_VER +#include +#endif // ! _MSC_VER +#include "pcap.h" +#include +#include "Logger.h" +#include "SystemUtils.h" +#include +#include +#include +#include +#if defined(_WIN32) +// The definition of BPF_MAJOR_VERSION is required to support Npcap. In Npcap there are +// compilation errors due to struct redefinition when including both Packet32.h and pcap.h +// This define statement eliminates these errors +#ifndef BPF_MAJOR_VERSION +#define BPF_MAJOR_VERSION 1 +#endif // BPF_MAJOR_VERSION +#include +#include +#include +#include +#else +#include +#include +#include +#endif // if defined(_WIN32) +#if defined(__APPLE__) || defined(__FreeBSD__) +#include +#include +#endif + +// On Mac OS X and FreeBSD timeout of -1 causes pcap_open_live to fail so value of 1ms is set here. +// On Linux and Windows this is not the case so we keep the -1 value +#if defined(__APPLE__) || defined(__FreeBSD__) +#define LIBPCAP_OPEN_LIVE_TIMEOUT 1 +#else +#define LIBPCAP_OPEN_LIVE_TIMEOUT -1 +#endif + +static const int DEFAULT_SNAPLEN = 9000; + +namespace pcpp +{ + +#ifdef HAS_SET_DIRECTION_ENABLED +static pcap_direction_t directionTypeMap(PcapLiveDevice::PcapDirection direction) +{ + switch (direction) + { + case PcapLiveDevice::PCPP_IN: return PCAP_D_IN; + case PcapLiveDevice::PCPP_OUT: return PCAP_D_OUT; + case PcapLiveDevice::PCPP_INOUT: return PCAP_D_INOUT; + } +} +#endif + + + +PcapLiveDevice::PcapLiveDevice(pcap_if_t* pInterface, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway) : IPcapDevice(), + m_MacAddress(""), m_DefaultGateway(IPv4Address::Zero) +{ + m_DeviceMtu = 0; + m_LinkType = LINKTYPE_ETHERNET; + + m_IsLoopback = (pInterface->flags & 0x1) == PCAP_IF_LOOPBACK; + + m_Name = pInterface->name; + if (pInterface->description != nullptr) + m_Description = pInterface->description; + PCPP_LOG_DEBUG("Added live device: name=" << m_Name << "; desc=" << m_Description); + PCPP_LOG_DEBUG(" Addresses:"); + while (pInterface->addresses != nullptr) + { + m_Addresses.insert(m_Addresses.end(), *(pInterface->addresses)); + pInterface->addresses = pInterface->addresses->next; + if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && pInterface->addresses != nullptr && pInterface->addresses->addr != nullptr) + { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(pInterface->addresses->addr, addrAsString); + PCPP_LOG_DEBUG(" " << addrAsString); + } + } + + if (calculateMTU) + { + setDeviceMtu(); + PCPP_LOG_DEBUG(" MTU: " << m_DeviceMtu); + } + + if (calculateDefaultGateway) + { + setDefaultGateway(); + PCPP_LOG_DEBUG(" Default Gateway: " << m_DefaultGateway); + } + + //init all other members + m_CaptureThreadStarted = false; + m_StatsThreadStarted = false; + m_IsLoopback = false; + m_StopThread = false; + m_CaptureThread = {}; + m_StatsThread = {}; + m_cbOnPacketArrives = nullptr; + m_cbOnStatsUpdate = nullptr; + m_cbOnPacketArrivesBlockingMode = nullptr; + m_cbOnPacketArrivesBlockingModeUserCookie = nullptr; + m_IntervalToUpdateStats = 0; + m_cbOnPacketArrivesUserCookie = nullptr; + m_cbOnStatsUpdateUserCookie = nullptr; + m_CaptureCallbackMode = true; + m_CapturedPackets = nullptr; + if (calculateMacAddress) + { + setDeviceMacAddress(); + if (m_MacAddress.isValid()) + PCPP_LOG_DEBUG(" MAC addr: " << m_MacAddress); + } +} + +void PcapLiveDevice::onPacketArrives(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet) +{ + PcapLiveDevice* pThis = (PcapLiveDevice*)user; + if (pThis == nullptr) + { + PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); + return; + } + + RawPacket rawPacket(packet, pkthdr->caplen, pkthdr->ts, false, pThis->getLinkType()); + + if (pThis->m_cbOnPacketArrives != nullptr) + pThis->m_cbOnPacketArrives(&rawPacket, pThis, pThis->m_cbOnPacketArrivesUserCookie); +} + +void PcapLiveDevice::onPacketArrivesNoCallback(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet) +{ + PcapLiveDevice* pThis = (PcapLiveDevice*)user; + if (pThis == nullptr) + { + PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); + return; + } + + uint8_t* packetData = new uint8_t[pkthdr->caplen]; + memcpy(packetData, packet, pkthdr->caplen); + RawPacket* rawPacketPtr = new RawPacket(packetData, pkthdr->caplen, pkthdr->ts, true, pThis->getLinkType()); + pThis->m_CapturedPackets->pushBack(rawPacketPtr); +} + +void PcapLiveDevice::onPacketArrivesBlockingMode(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet) +{ + PcapLiveDevice* pThis = (PcapLiveDevice*)user; + if (pThis == nullptr) + { + PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); + return; + } + + RawPacket rawPacket(packet, pkthdr->caplen, pkthdr->ts, false, pThis->getLinkType()); + + if (pThis->m_cbOnPacketArrivesBlockingMode != nullptr) + if (pThis->m_cbOnPacketArrivesBlockingMode(&rawPacket, pThis, pThis->m_cbOnPacketArrivesBlockingModeUserCookie)) + pThis->m_StopThread = true; +} + +void PcapLiveDevice::captureThreadMain() +{ + PCPP_LOG_DEBUG("Started capture thread for device '" << m_Name << "'"); + if (m_CaptureCallbackMode) + { + while (!m_StopThread) + pcap_dispatch(m_PcapDescriptor, -1, onPacketArrives, (uint8_t*)this); + } + else + { + while (!m_StopThread) + pcap_dispatch(m_PcapDescriptor, 100, onPacketArrivesNoCallback, (uint8_t*)this); + } + PCPP_LOG_DEBUG("Ended capture thread for device '" << m_Name << "'"); +} + +void PcapLiveDevice::statsThreadMain() +{ + PCPP_LOG_DEBUG("Started stats thread for device '" << m_Name << "'"); + while (!m_StopThread) + { + PcapStats stats; + getStatistics(stats); + m_cbOnStatsUpdate(stats, m_cbOnStatsUpdateUserCookie); + multiPlatformSleep(m_IntervalToUpdateStats); + } + PCPP_LOG_DEBUG("Ended stats thread for device '" << m_Name << "'"); +} + +pcap_t* PcapLiveDevice::doOpen(const DeviceConfiguration& config) +{ + char errbuf[PCAP_ERRBUF_SIZE] = {'\0'}; + pcap_t* pcap = pcap_create(m_Name.c_str(), errbuf); + if (!pcap) + { + PCPP_LOG_ERROR(errbuf); + return pcap; + } + int ret = pcap_set_snaplen(pcap, config.snapshotLength <= 0 ? DEFAULT_SNAPLEN : config.snapshotLength); + if (ret != 0) + { + PCPP_LOG_ERROR(pcap_geterr(pcap)); + } + ret = pcap_set_promisc(pcap, config.mode); + if (ret != 0) + { + PCPP_LOG_ERROR(pcap_geterr(pcap)); + } + + int timeout = (config.packetBufferTimeoutMs <= 0 ? LIBPCAP_OPEN_LIVE_TIMEOUT : config.packetBufferTimeoutMs); + ret = pcap_set_timeout(pcap, timeout); + if (ret != 0) + { + PCPP_LOG_ERROR(pcap_geterr(pcap)); + } + + if (config.packetBufferSize >= 100) + { + ret = pcap_set_buffer_size(pcap, config.packetBufferSize); + if (ret != 0) + { + PCPP_LOG_ERROR(pcap_geterr(pcap)); + } + } + +#ifdef HAS_PCAP_IMMEDIATE_MODE + ret = pcap_set_immediate_mode(pcap, 1); + if (ret == 0) + { + PCPP_LOG_DEBUG("Immediate mode is activated"); + } + else + { + PCPP_LOG_ERROR("Failed to activate immediate mode, error code: '" << ret << "', error message: '" << pcap_geterr(pcap) << "'"); + } +#endif + + ret = pcap_activate(pcap); + if (ret != 0) + { + PCPP_LOG_ERROR(pcap_geterr(pcap)); + pcap_close(pcap); + return nullptr; + } + +#ifdef HAS_SET_DIRECTION_ENABLED + pcap_direction_t directionToSet = directionTypeMap(config.direction); + ret = pcap_setdirection(pcap, directionToSet); + if (ret == 0) + { + if (config.direction == PCPP_IN) + { + PCPP_LOG_DEBUG("Only incoming traffics will be captured"); + } + else if (config.direction == PCPP_OUT) + { + PCPP_LOG_DEBUG("Only outgoing traffics will be captured"); + } + else + { + PCPP_LOG_DEBUG("Both incoming and outgoing traffics will be captured"); + } + } + else + { + PCPP_LOG_ERROR("Failed to set direction for capturing packets, error code: '" << ret << "', error message: '" << pcap_geterr(pcap) << "'"); + } +#endif + + if (pcap) + { + int dlt = pcap_datalink(pcap); + const char* dlt_name = pcap_datalink_val_to_name(dlt); + if (dlt_name) + { + PCPP_LOG_DEBUG("link-type " << dlt << ": " << dlt_name << " (" << pcap_datalink_val_to_description(dlt) << ")"); + } + else + { + PCPP_LOG_DEBUG("link-type " << dlt); + } + + m_LinkType = static_cast(dlt); + } + return pcap; +} + +bool PcapLiveDevice::open(const DeviceConfiguration& config) +{ + if (m_DeviceOpened) + { + PCPP_LOG_DEBUG("Device '" << m_Name << "' already opened"); + return true; + } + + m_PcapDescriptor = doOpen(config); + m_PcapSendDescriptor = doOpen(config); + if (m_PcapDescriptor == nullptr || m_PcapSendDescriptor == nullptr) + { + m_DeviceOpened = false; + return false; + } + + PCPP_LOG_DEBUG("Device '" << m_Name << "' opened"); + + m_DeviceOpened = true; + + return true; +} + +bool PcapLiveDevice::open() +{ + DeviceConfiguration defaultConfig; + return open(defaultConfig); +} + +void PcapLiveDevice::close() +{ + if (m_PcapDescriptor == nullptr && m_PcapSendDescriptor == nullptr) + { + PCPP_LOG_DEBUG("Device '" << m_Name << "' already closed"); + return; + } + + bool sameDescriptor = (m_PcapDescriptor == m_PcapSendDescriptor); + pcap_close(m_PcapDescriptor); + PCPP_LOG_DEBUG("Receive pcap descriptor closed"); + if (!sameDescriptor) + { + pcap_close(m_PcapSendDescriptor); + PCPP_LOG_DEBUG("Send pcap descriptor closed"); + } + + m_DeviceOpened = false; + PCPP_LOG_DEBUG("Device '" << m_Name << "' closed"); +} + +PcapLiveDevice* PcapLiveDevice::clone() +{ + PcapLiveDevice *retval = nullptr; + + pcap_if_t *interfaceList; + char errbuf[PCAP_ERRBUF_SIZE]; + int err = pcap_findalldevs(&interfaceList, errbuf); + if (err < 0) + { + PCPP_LOG_ERROR("Error searching for devices: " << errbuf); + return nullptr; + } + + pcap_if_t* currInterface = interfaceList; + while (currInterface != nullptr) + { + if(!strcmp(currInterface->name, getName().c_str())) + break; + currInterface = currInterface->next; + } + + if(currInterface) + retval = new PcapLiveDevice(currInterface, true, true, true); + else + PCPP_LOG_ERROR("Can't find interface " << getName().c_str()); + + pcap_freealldevs(interfaceList); + return retval; +} + +bool PcapLiveDevice::startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie) +{ + return startCapture(onPacketArrives, onPacketArrivesUserCookie, 0, nullptr, nullptr); +} + +bool PcapLiveDevice::startCapture(int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie) +{ + return startCapture(nullptr, nullptr, intervalInSecondsToUpdateStats, onStatsUpdate, onStatsUpdateUserCookie); +} + +bool PcapLiveDevice::startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie, int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie) +{ + if (!m_DeviceOpened || m_PcapDescriptor == nullptr) + { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); + return false; + } + + if (m_CaptureThreadStarted) + { + PCPP_LOG_ERROR("Device '" << m_Name << "' already capturing traffic"); + return false; + } + + m_IntervalToUpdateStats = intervalInSecondsToUpdateStats; + + m_CaptureCallbackMode = true; + m_cbOnPacketArrives = onPacketArrives; + m_cbOnPacketArrivesUserCookie = onPacketArrivesUserCookie; + + m_CaptureThread = std::thread(&pcpp::PcapLiveDevice::captureThreadMain, this); + m_CaptureThreadStarted = true; + PCPP_LOG_DEBUG("Successfully created capture thread for device '" << m_Name << "'. Thread id: " << m_CaptureThread.get_id()); + + if (onStatsUpdate != nullptr && intervalInSecondsToUpdateStats > 0) + { + m_cbOnStatsUpdate = onStatsUpdate; + m_cbOnStatsUpdateUserCookie = onStatsUpdateUserCookie; + m_StatsThread = std::thread(&pcpp::PcapLiveDevice::statsThreadMain, this); + m_StatsThreadStarted = true; + PCPP_LOG_DEBUG("Successfully created stats thread for device '" << m_Name << "'. Thread id: " << m_StatsThread.get_id()); + } + + return true; +} + +bool PcapLiveDevice::startCapture(RawPacketVector& capturedPacketsVector) +{ + if (!m_DeviceOpened || m_PcapDescriptor == nullptr) + { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); + return false; + } + + if (m_CaptureThreadStarted) + { + PCPP_LOG_ERROR("Device '" << m_Name << "' already capturing traffic"); + return false; + } + + m_CapturedPackets = &capturedPacketsVector; + m_CapturedPackets->clear(); + + m_CaptureCallbackMode = false; + m_CaptureThread = std::thread(&pcpp::PcapLiveDevice::captureThreadMain, this); + m_CaptureThreadStarted = true; + PCPP_LOG_DEBUG("Successfully created capture thread for device '" << m_Name << "'. Thread id: " << m_CaptureThread.get_id()); + + return true; +} + + +int PcapLiveDevice::startCaptureBlockingMode(OnPacketArrivesStopBlocking onPacketArrives, void* userCookie, int timeout) +{ + if (!m_DeviceOpened || m_PcapDescriptor == nullptr) + { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); + return 0; + } + + if (m_CaptureThreadStarted) + { + PCPP_LOG_ERROR("Device '" << m_Name << "' already capturing traffic"); + return 0; + } + + m_cbOnPacketArrives = nullptr; + m_cbOnStatsUpdate = nullptr; + m_cbOnPacketArrivesUserCookie = nullptr; + m_cbOnStatsUpdateUserCookie = nullptr; + + m_cbOnPacketArrivesBlockingMode = onPacketArrives; + m_cbOnPacketArrivesBlockingModeUserCookie = userCookie; + + long startTimeSec = 0, startTimeNSec = 0; + clockGetTime(startTimeSec, startTimeNSec); + + long curTimeSec = 0; + + m_CaptureThreadStarted = true; + m_StopThread = false; + + if (timeout <= 0) + { + while (!m_StopThread) + { + pcap_dispatch(m_PcapDescriptor, -1, onPacketArrivesBlockingMode, (uint8_t*)this); + } + curTimeSec = startTimeSec + timeout; + } + else + { + while (!m_StopThread && curTimeSec <= (startTimeSec + timeout)) + { + long curTimeNSec = 0; + pcap_dispatch(m_PcapDescriptor, -1, onPacketArrivesBlockingMode, (uint8_t*)this); + clockGetTime(curTimeSec, curTimeNSec); + } + } + + m_CaptureThreadStarted = false; + + m_StopThread = false; + + m_cbOnPacketArrivesBlockingMode = nullptr; + m_cbOnPacketArrivesBlockingModeUserCookie = nullptr; + + if (curTimeSec > (startTimeSec + timeout)) + return -1; + return 1; +} + +void PcapLiveDevice::stopCapture() +{ + // in blocking mode stop capture isn't relevant + if (m_cbOnPacketArrivesBlockingMode != nullptr) + return; + + m_StopThread = true; + if (m_CaptureThreadStarted) + { + pcap_breakloop(m_PcapDescriptor); + PCPP_LOG_DEBUG("Stopping capture thread, waiting for it to join..."); + m_CaptureThread.join(); + m_CaptureThreadStarted = false; + PCPP_LOG_DEBUG("Capture thread stopped for device '" << m_Name << "'"); + } + PCPP_LOG_DEBUG("Capture thread stopped for device '" << m_Name << "'"); + if (m_StatsThreadStarted) + { + PCPP_LOG_DEBUG("Stopping stats thread, waiting for it to join..."); + m_StatsThread.join(); + m_StatsThreadStarted = false; + PCPP_LOG_DEBUG("Stats thread stopped for device '" << m_Name << "'"); + } + + multiPlatformSleep(1); + m_StopThread = false; +} + +bool PcapLiveDevice::captureActive() +{ + return m_CaptureThreadStarted; +} + +void PcapLiveDevice::getStatistics(PcapStats& stats) const +{ + pcap_stat pcapStats; + if (pcap_stats(m_PcapDescriptor, &pcapStats) < 0) + { + PCPP_LOG_ERROR("Error getting statistics from live device '" << m_Name << "'"); + } + + stats.packetsRecv = pcapStats.ps_recv; + stats.packetsDrop = pcapStats.ps_drop; + stats.packetsDropByInterface = pcapStats.ps_ifdrop; +} + +bool PcapLiveDevice::doMtuCheck(int packetPayloadLength) +{ + if (packetPayloadLength > (int)m_DeviceMtu) + { + PCPP_LOG_ERROR("Payload length [" << packetPayloadLength << "] is larger than device MTU [" << m_DeviceMtu << "]"); + return false; + } + return true; +} + +bool PcapLiveDevice::sendPacket(RawPacket const& rawPacket, bool checkMtu) +{ + if (checkMtu) + { + RawPacket *rPacket = (RawPacket *)&rawPacket; + Packet parsedPacket = Packet(rPacket, OsiModelDataLinkLayer); + return sendPacket(&parsedPacket, true); + } + // Send packet without Mtu check + return sendPacket(((RawPacket&)rawPacket).getRawData(), ((RawPacket&)rawPacket).getRawDataLen()); +} + +bool PcapLiveDevice::sendPacket(const uint8_t* packetData, int packetDataLength, int packetPayloadLength) +{ + return doMtuCheck(packetPayloadLength) && sendPacket(packetData, packetDataLength); +} + +bool PcapLiveDevice::sendPacket(const uint8_t* packetData, int packetDataLength, bool checkMtu, pcpp::LinkLayerType linkType) +{ + if (checkMtu) + { + timeval time; + gettimeofday(&time, nullptr); + pcpp::RawPacket rawPacket(packetData, packetDataLength, time, false, linkType); + Packet parsedPacket = Packet(&rawPacket, pcpp::OsiModelDataLinkLayer); + return sendPacket(&parsedPacket, true); + } + + if (!m_DeviceOpened) + { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened!"); + return false; + } + + if (packetDataLength == 0) + { + PCPP_LOG_ERROR("Trying to send a packet with length 0"); + return false; + } + + if (pcap_sendpacket(m_PcapSendDescriptor, packetData, packetDataLength) == -1) + { + PCPP_LOG_ERROR("Error sending packet: " << pcap_geterr(m_PcapSendDescriptor)); + return false; + } + + PCPP_LOG_DEBUG("Packet sent successfully. Packet length: " << packetDataLength); + return true; +} + +bool PcapLiveDevice::sendPacket(Packet* packet, bool checkMtu) +{ + RawPacket* rawPacket = packet->getRawPacket(); + if (checkMtu) + { + int packetPayloadLength = 0; + switch (packet->getFirstLayer()->getOsiModelLayer()) + { + case (pcpp::OsiModelDataLinkLayer): + packetPayloadLength = (int)packet->getFirstLayer()->getLayerPayloadSize(); + break; + case (pcpp::OsiModelNetworkLayer): + packetPayloadLength = (int)packet->getFirstLayer()->getDataLen(); + break; + default: + // if packet layer is not known, do not perform MTU check. + return sendPacket(*rawPacket, false); + } + return doMtuCheck(packetPayloadLength) && sendPacket(*rawPacket, false); + } + return sendPacket(*rawPacket, false); +} + +int PcapLiveDevice::sendPackets(RawPacket* rawPacketsArr, int arrLength, bool checkMtu) +{ + int packetsSent = 0; + for (int i = 0; i < arrLength; i++) + { + if (sendPacket(rawPacketsArr[i], checkMtu)) + packetsSent++; + } + + PCPP_LOG_DEBUG(packetsSent << " packets sent successfully. " << arrLength-packetsSent << " packets not sent"); + return packetsSent; +} + +int PcapLiveDevice::sendPackets(Packet** packetsArr, int arrLength, bool checkMtu) +{ + int packetsSent = 0; + for (int i = 0; i < arrLength; i++) + { + if (sendPacket(packetsArr[i], checkMtu)) + packetsSent++; + } + + PCPP_LOG_DEBUG(packetsSent << " packets sent successfully. " << arrLength-packetsSent << " packets not sent"); + return packetsSent; +} + +int PcapLiveDevice::sendPackets(const RawPacketVector& rawPackets, bool checkMtu) +{ + int packetsSent = 0; + for (RawPacketVector::ConstVectorIterator iter = rawPackets.begin(); iter != rawPackets.end(); iter++) + { + if (sendPacket(**iter, checkMtu)) + packetsSent++; + } + + PCPP_LOG_DEBUG(packetsSent << " packets sent successfully. " << (rawPackets.size()-packetsSent) << " packets not sent"); + return packetsSent; +} + +void PcapLiveDevice::setDeviceMtu() +{ +#if defined(_WIN32) + + if (m_IsLoopback) + { + PCPP_LOG_DEBUG("Npcap Loopback Adapter - MTU is insignificant, setting MTU to max value (0xffffffff)"); + m_DeviceMtu = 0xffffffff; + return; + } + + uint32_t mtuValue = 0; + LPADAPTER adapter = PacketOpenAdapter((char*)m_Name.c_str()); + if (adapter == NULL) + { + PCPP_LOG_ERROR("Error in retrieving MTU: Adapter is NULL"); + return; + } + + uint8_t buffer[512]; + PACKET_OID_DATA* oidData = (PACKET_OID_DATA*)buffer; + oidData->Oid = OID_GEN_MAXIMUM_TOTAL_SIZE; + oidData->Length = sizeof(uint32_t); + memcpy(oidData->Data, &mtuValue, sizeof(uint32_t)); + if (PacketRequest(adapter, false, oidData)) + { + if (oidData->Length <= sizeof(uint32_t)) + { + /* copy value from driver */ + memcpy(&mtuValue, oidData->Data, oidData->Length); + // Sometimes the query gives a wrong number that includes the link header size + // A very common value is 1514 - if identify this value just reduce to 1500. + // TODO: think of a better way to always get the right value + if (mtuValue == 1514) + { + mtuValue = 1500; + } + m_DeviceMtu = mtuValue; + } + else + { + /* the driver returned a value that is longer than expected (and longer than the given buffer) */ + PCPP_LOG_ERROR("Error in retrieving MTU: Size of Oid larger than uint32_t, OidLen: " << oidData->Length); + return; + } + } + else + { + PCPP_LOG_ERROR("Error in retrieving MTU: PacketRequest failed"); + } + +#else + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, m_Name.c_str(), sizeof(ifr.ifr_name) - 1); + + int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (ioctl(socketfd, SIOCGIFMTU, &ifr) == -1) + { + PCPP_LOG_DEBUG("Error in retrieving MTU: ioctl() returned -1"); + m_DeviceMtu = 0; + return; + } + + m_DeviceMtu = ifr.ifr_mtu; +#endif +} + +void PcapLiveDevice::setDeviceMacAddress() +{ +#if defined(_WIN32) + + LPADAPTER adapter = PacketOpenAdapter((char*)m_Name.c_str()); + if (adapter == NULL) + { + PCPP_LOG_ERROR("Error in retrieving MAC address: Adapter is NULL"); + return; + } + + uint8_t buffer[512]; + PACKET_OID_DATA* oidData = (PACKET_OID_DATA*)buffer; + oidData->Oid = OID_802_3_CURRENT_ADDRESS; + oidData->Length = 6; + oidData->Data[0] = 0; + if (PacketRequest(adapter, false, oidData)) + { + if (oidData->Length == 6) + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" + /* copy value from driver */ + m_MacAddress = MacAddress(oidData->Data[0], oidData->Data[1], oidData->Data[2], oidData->Data[3], oidData->Data[4], oidData->Data[5]); +#pragma GCC diagnostic pop + PCPP_LOG_DEBUG(" MAC address: " << m_MacAddress); + } + else + { + /* the driver returned a value that is longer than expected (and longer than the given buffer) */ + PCPP_LOG_DEBUG("Error in retrieving MAC address: Size of Oid larger than 6, OidLen: " << oidData->Length); + return; + } + } + else + { + PCPP_LOG_DEBUG("Error in retrieving MAC address: PacketRequest failed"); + } +#elif defined(__linux__) + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, m_Name.c_str(), sizeof(ifr.ifr_name) - 1); + + int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (ioctl(socketfd, SIOCGIFHWADDR, &ifr) == -1) + { + PCPP_LOG_DEBUG("Error in retrieving MAC address: ioctl() returned -1"); + return; + } + + m_MacAddress = MacAddress(ifr.ifr_hwaddr.sa_data[0], ifr.ifr_hwaddr.sa_data[1], ifr.ifr_hwaddr.sa_data[2], ifr.ifr_hwaddr.sa_data[3], ifr.ifr_hwaddr.sa_data[4], ifr.ifr_hwaddr.sa_data[5]); +#elif defined(__APPLE__) || defined(__FreeBSD__) + int mib[6]; + size_t len; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_LINK; + mib[4] = NET_RT_IFLIST; + mib[5] = if_nametoindex(m_Name.c_str()); + + if (mib[5] == 0){ + PCPP_LOG_DEBUG("Error in retrieving MAC address: if_nametoindex error"); + return; + } + + if (sysctl(mib, 6, nullptr, &len, nullptr, 0) < 0) + { + PCPP_LOG_DEBUG("Error in retrieving MAC address: sysctl 1 error"); + return; + } + + uint8_t buf[len]; + + if (sysctl(mib, 6, buf, &len, nullptr, 0) < 0) + { + PCPP_LOG_DEBUG("Error in retrieving MAC address: sysctl 2 error"); + return; + } + + struct if_msghdr*ifm = (struct if_msghdr *)buf; + struct sockaddr_dl* sdl = (struct sockaddr_dl *)(ifm + 1); + uint8_t* ptr = (uint8_t*)LLADDR(sdl); + m_MacAddress = MacAddress(ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); +#endif +} + +void PcapLiveDevice::setDefaultGateway() +{ +#if defined(_WIN32) + ULONG outBufLen = sizeof (IP_ADAPTER_INFO); + uint8_t* buffer = new uint8_t[outBufLen]; + PIP_ADAPTER_INFO adapterInfo = (IP_ADAPTER_INFO*)buffer; + DWORD retVal = 0; + + retVal = GetAdaptersInfo(adapterInfo, &outBufLen); + uint8_t* buffer2 = new uint8_t[outBufLen]; + if (retVal == ERROR_BUFFER_OVERFLOW) + adapterInfo = (IP_ADAPTER_INFO *)buffer2; + + retVal = GetAdaptersInfo(adapterInfo, &outBufLen); + + if (retVal == NO_ERROR) + { + PIP_ADAPTER_INFO curAdapterInfo = adapterInfo; + while (curAdapterInfo != NULL) + { + if (m_Name.find(curAdapterInfo->AdapterName) != std::string::npos) + m_DefaultGateway = IPv4Address(curAdapterInfo->GatewayList.IpAddress.String); + + curAdapterInfo = curAdapterInfo->Next; + } + } + else + { + PCPP_LOG_ERROR("Error retrieving default gateway address"); + } + + delete[] buffer; + // cppcheck-suppress uninitdata + delete[] buffer2; +#elif defined(__linux__) + std::ifstream routeFile("/proc/net/route"); + std::string line; + while (std::getline(routeFile, line)) + { + std::stringstream lineStream(line); + std::string interfaceName; + std::getline(lineStream, interfaceName, '\t'); + if (interfaceName != m_Name) + continue; + + std::string interfaceDest; + std::getline(lineStream, interfaceDest, '\t'); + if (interfaceDest != "00000000") + continue; + + std::string interfaceGateway; + std::getline(lineStream, interfaceGateway, '\t'); + + uint32_t interfaceGatewayIPInt; + std::stringstream interfaceGatewayStream; + interfaceGatewayStream << std::hex << interfaceGateway; + interfaceGatewayStream >> interfaceGatewayIPInt; + m_DefaultGateway = IPv4Address(interfaceGatewayIPInt); + } +#elif defined(__APPLE__) || defined(__FreeBSD__) + std::string command = "netstat -nr | grep default | grep " + m_Name; + std::string ifaceInfo = executeShellCommand(command); + if (ifaceInfo == "") + { + PCPP_LOG_DEBUG("Error retrieving default gateway address: couldn't get netstat output"); + return; + } + + // remove the word "default" + ifaceInfo.erase(0, 7); + + // remove spaces + while (ifaceInfo.at(0) == ' ') + ifaceInfo.erase(0,1); + + // erase string after gateway IP address + ifaceInfo.resize(ifaceInfo.find(' ', 0)); + + m_DefaultGateway = IPv4Address(ifaceInfo); +#endif +} + +IPv4Address PcapLiveDevice::getIPv4Address() const +{ + for(std::vector::const_iterator addrIter = m_Addresses.begin(); addrIter != m_Addresses.end(); addrIter++) + { + if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter->addr != nullptr) + { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(addrIter->addr, addrAsString); + PCPP_LOG_DEBUG("Searching address " << addrAsString); + } + + in_addr* currAddr = internal::sockaddr2in_addr(addrIter->addr); + if (currAddr == nullptr) + { + PCPP_LOG_DEBUG("Address is NULL"); + continue; + } + + return IPv4Address(currAddr->s_addr); + } + + return IPv4Address::Zero; +} + +IPv6Address PcapLiveDevice::getIPv6Address() const +{ + for (std::vector::const_iterator addrIter = m_Addresses.begin(); addrIter != m_Addresses.end(); + addrIter++) + { + if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter->addr != nullptr) + { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(addrIter->addr, addrAsString); + PCPP_LOG_DEBUG("Searching address " << addrAsString); + } + in6_addr *currAddr = internal::sockaddr2in6_addr(addrIter->addr); + if (currAddr == nullptr) + { + PCPP_LOG_DEBUG("Address is NULL"); + continue; + } + return IPv6Address(currAddr->s6_addr); + } + return IPv6Address::Zero; +} + +IPv4Address PcapLiveDevice::getDefaultGateway() const +{ + return m_DefaultGateway; +} + +const std::vector& PcapLiveDevice::getDnsServers() const +{ + return PcapLiveDeviceList::getInstance().getDnsServers(); +} + +PcapLiveDevice::~PcapLiveDevice() +{ +} + +} // namespace pcpp diff --git a/Pcap++/src/PcapLiveDeviceList.cpp b/Pcap++/src/PcapLiveDeviceList.cpp index d74b7a83a8..c6da146dfe 100644 --- a/Pcap++/src/PcapLiveDeviceList.cpp +++ b/Pcap++/src/PcapLiveDeviceList.cpp @@ -1,359 +1,359 @@ -#define LOG_MODULE PcapLogModuleLiveDevice - -#include "IpUtils.h" -#include "PcapLiveDeviceList.h" -#include "Logger.h" -#include "SystemUtils.h" -#include "pcap.h" -#include -#include -#include -#if defined(_WIN32) -#include -#include -#include "WinPcapLiveDevice.h" -#elif defined(__APPLE__) -#include -#elif defined(__FreeBSD__) -#include -#include -#endif - - -namespace pcpp -{ - -PcapLiveDeviceList::PcapLiveDeviceList() -{ - init(); -} - -PcapLiveDeviceList::~PcapLiveDeviceList() -{ - for(std::vector::iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) - { - delete (*devIter); - } -} - -void PcapLiveDeviceList::init() -{ - pcap_if_t* interfaceList; - char errbuf[PCAP_ERRBUF_SIZE]; - int err = pcap_findalldevs(&interfaceList, errbuf); - if (err < 0) - { - PCPP_LOG_ERROR("Error searching for devices: " << errbuf); - } - - PCPP_LOG_DEBUG("Pcap lib version info: " << IPcapDevice::getPcapLibVersionInfo()); - - pcap_if_t* currInterface = interfaceList; - while (currInterface != nullptr) - { -#if defined(_WIN32) - PcapLiveDevice* dev = new WinPcapLiveDevice(currInterface, true, true, true); -#else //__linux__, __APPLE__, __FreeBSD__ - PcapLiveDevice* dev = new PcapLiveDevice(currInterface, true, true, true); -#endif - currInterface = currInterface->next; - m_LiveDeviceList.insert(m_LiveDeviceList.end(), dev); - } - - setDnsServers(); - - PCPP_LOG_DEBUG("Freeing live device data"); - pcap_freealldevs(interfaceList); -} - -void PcapLiveDeviceList::setDnsServers() -{ -#if defined(_WIN32) - FIXED_INFO * fixedInfo; - ULONG ulOutBufLen; - DWORD dwRetVal; - IP_ADDR_STRING * pIPAddr; - - uint8_t buf1[sizeof(FIXED_INFO)]; - fixedInfo = (FIXED_INFO *) buf1; - ulOutBufLen = sizeof( FIXED_INFO ); - - dwRetVal = GetNetworkParams( fixedInfo, &ulOutBufLen ); - uint8_t* buf2 = new uint8_t[ulOutBufLen]; - if(ERROR_BUFFER_OVERFLOW == dwRetVal) - { - fixedInfo = (FIXED_INFO *)buf2; - } - - if ((dwRetVal = GetNetworkParams( fixedInfo, &ulOutBufLen )) != 0) - PCPP_LOG_ERROR("Call to GetNetworkParams failed. Return Value: " << std::hex << dwRetVal); - else - { - m_DnsServers.push_back(IPv4Address(fixedInfo->DnsServerList.IpAddress.String)); - int i = 1; - PCPP_LOG_DEBUG("Default DNS server IP #" << i++ << ": " << fixedInfo->DnsServerList.IpAddress.String); - - pIPAddr = fixedInfo->DnsServerList.Next; - while ( pIPAddr ) - { - m_DnsServers.push_back(IPv4Address(pIPAddr->IpAddress.String)); - PCPP_LOG_DEBUG("Default DNS server IP #" << i++ << ": " << pIPAddr->IpAddress.String); - pIPAddr = pIPAddr -> Next; - } - } - - delete[] buf2; -#elif defined(__linux__) - // verify that nmcli exist - std::string command = "command -v nmcli >/dev/null 2>&1 || { echo 'nmcli not installed'; }"; - std::string nmcliExists = executeShellCommand(command); - if (nmcliExists != "") - { - PCPP_LOG_DEBUG("Error retrieving DNS server list: nmcli doesn't exist"); - return; - } - - // check nmcli major version (0 or 1) - command = "nmcli -v | awk -F' ' '{print $NF}' | awk -F'.' '{print $1}'"; - std::string nmcliMajorVer = executeShellCommand(command); - nmcliMajorVer.erase(std::remove(nmcliMajorVer.begin(), nmcliMajorVer.end(), '\n'), nmcliMajorVer.end()); - PCPP_LOG_DEBUG("Found nmcli. nmcli major version is: '" << nmcliMajorVer << "'"); - - // build nmcli command according to its major version - if (nmcliMajorVer == "0") - command = "nmcli dev list | grep IP4.DNS"; - else - command = "nmcli dev show | grep IP4.DNS"; - - std::string dnsServersInfo = executeShellCommand(command); - if (dnsServersInfo == "") - { - PCPP_LOG_DEBUG("Error retrieving DNS server list: call to nmcli gave no output"); - return; - } - - std::istringstream stream(dnsServersInfo); - std::string line; - int i = 1; - while(std::getline(stream, line)) - { - std::istringstream lineStream(line); - std::string headline; - std::string dnsIP; - lineStream >> headline; - lineStream >> dnsIP; - IPv4Address dnsIPAddr(dnsIP); - if (!dnsIPAddr.isValid()) - continue; - - if (std::find(m_DnsServers.begin(), m_DnsServers.end(), dnsIPAddr) == m_DnsServers.end()) - { - m_DnsServers.push_back(dnsIPAddr); - PCPP_LOG_DEBUG("Default DNS server IP #" << i++ << ": " << dnsIPAddr); - } - } -#elif defined(__APPLE__) - - SCDynamicStoreRef dynRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault, CFSTR("iked"), nullptr, nullptr); - if (dynRef == nullptr) - { - PCPP_LOG_DEBUG("Couldn't set DNS server list: failed to retrieve SCDynamicStore"); - return; - } - - CFDictionaryRef dnsDict = (CFDictionaryRef)SCDynamicStoreCopyValue(dynRef,CFSTR("State:/Network/Global/DNS")); - - if (dnsDict == nullptr) - { - PCPP_LOG_DEBUG("Couldn't set DNS server list: failed to get DNS dictionary"); - CFRelease(dynRef); - return; - } - - CFArrayRef serverAddresses = (CFArrayRef)CFDictionaryGetValue(dnsDict, CFSTR("ServerAddresses")); - - if (serverAddresses == nullptr) - { - PCPP_LOG_DEBUG("Couldn't set DNS server list: server addresses array is null"); - CFRelease(dynRef); - CFRelease(dnsDict); - return; - } - - CFIndex count = CFArrayGetCount(serverAddresses); - for (CFIndex i = 0; i < count; i++) - { - CFStringRef serverAddress = (CFStringRef)CFArrayGetValueAtIndex(serverAddresses, i); - - if (serverAddress == nullptr) - continue; - - uint8_t buf[20]; - char* serverAddressCString = (char*)buf; - CFStringGetCString(serverAddress, serverAddressCString, 20, kCFStringEncodingUTF8); - m_DnsServers.push_back(IPv4Address(serverAddressCString)); - PCPP_LOG_DEBUG("Default DNS server IP #" << (int)(i+1) << ": " << serverAddressCString); - } - - CFRelease(dynRef); - CFRelease(dnsDict); - -#elif defined(__FreeBSD__) - - res_init(); - - for (int i = 0; i < _res.nscount; i++) - { - sockaddr* saddr = (sockaddr*)&_res.nsaddr_list[i]; - if (saddr == NULL) - continue; - in_addr* inaddr = internal::sockaddr2in_addr(saddr); - if (inaddr == NULL) - continue; - m_DnsServers.push_back(IPv4Address(internal::in_addr2int(*inaddr))); - } - -#endif -} - -PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPAddress& ipAddr) const -{ - if (ipAddr.getType() == IPAddress::IPv4AddressType) - { - return getPcapLiveDeviceByIp(ipAddr.getIPv4()); - } - else //IPAddress::IPv6AddressType - { - return getPcapLiveDeviceByIp(ipAddr.getIPv6()); - } -} - -PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPv4Address& ipAddr) const -{ - PCPP_LOG_DEBUG("Searching all live devices..."); - for(std::vector::const_iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) - { - PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name << "'. Searching all addresses..."); - for(std::vector::iterator addrIter = (*devIter)->m_Addresses.begin(); addrIter != (*devIter)->m_Addresses.end(); addrIter++) - { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter->addr != nullptr) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter->addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); - } - - in_addr* currAddr = internal::sockaddr2in_addr(addrIter->addr); - if (currAddr == nullptr) - { - PCPP_LOG_DEBUG("Address is NULL"); - continue; - } - - if (currAddr->s_addr == ipAddr.toInt()) - { - PCPP_LOG_DEBUG("Found matched address!"); - return (*devIter); - } - } - } - - return nullptr; -} - -PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPv6Address& ip6Addr) const -{ - PCPP_LOG_DEBUG("Searching all live devices..."); - for(std::vector::const_iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) - { - PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name << "'. Searching all addresses..."); - for(std::vector::iterator addrIter = (*devIter)->m_Addresses.begin(); addrIter != (*devIter)->m_Addresses.end(); addrIter++) - { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter->addr != nullptr) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter->addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); - } - - in6_addr* currAddr = internal::sockaddr2in6_addr(addrIter->addr); - if (currAddr == nullptr) - { - PCPP_LOG_DEBUG("Address is NULL"); - continue; - } - - uint8_t* addrAsArr; size_t addrLen; - ip6Addr.copyTo(&addrAsArr, addrLen); - if (memcmp(currAddr, addrAsArr, sizeof(struct in6_addr)) == 0) - { - PCPP_LOG_DEBUG("Found matched address!"); - delete [] addrAsArr; - return (*devIter); - } - - delete [] addrAsArr; - } - } - - return nullptr; -} - -PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const std::string& ipAddrAsString) const -{ - IPAddress ipAddr(ipAddrAsString); - if (!ipAddr.isValid()) - { - PCPP_LOG_ERROR("IP address illegal"); - return nullptr; - } - - PcapLiveDevice* result = PcapLiveDeviceList::getPcapLiveDeviceByIp(ipAddr); - return result; -} - - -PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByName(const std::string& name) const -{ - PCPP_LOG_DEBUG("Searching all live devices..."); - for(std::vector::const_iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) - { - if (name == (*devIter)->getName()) - return (*devIter); - } - - return nullptr; -} - -PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIpOrName(const std::string& ipOrName) const -{ - IPAddress interfaceIP(ipOrName); - if (interfaceIP.isValid()) - { - return PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(interfaceIP); - } - else - { - return PcapLiveDeviceList::getInstance().getPcapLiveDeviceByName(ipOrName); - } -} - -PcapLiveDeviceList* PcapLiveDeviceList::clone() -{ - return new PcapLiveDeviceList; -} - -void PcapLiveDeviceList::reset() -{ - for(std::vector::iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) - { - delete (*devIter); - } - - m_LiveDeviceList.clear(); - m_DnsServers.clear(); - - init(); -} - -} // namespace pcpp +#define LOG_MODULE PcapLogModuleLiveDevice + +#include "IpUtils.h" +#include "PcapLiveDeviceList.h" +#include "Logger.h" +#include "SystemUtils.h" +#include "pcap.h" +#include +#include +#include +#if defined(_WIN32) +#include +#include +#include "WinPcapLiveDevice.h" +#elif defined(__APPLE__) +#include +#elif defined(__FreeBSD__) +#include +#include +#endif + + +namespace pcpp +{ + +PcapLiveDeviceList::PcapLiveDeviceList() +{ + init(); +} + +PcapLiveDeviceList::~PcapLiveDeviceList() +{ + for(std::vector::iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) + { + delete (*devIter); + } +} + +void PcapLiveDeviceList::init() +{ + pcap_if_t* interfaceList; + char errbuf[PCAP_ERRBUF_SIZE]; + int err = pcap_findalldevs(&interfaceList, errbuf); + if (err < 0) + { + PCPP_LOG_ERROR("Error searching for devices: " << errbuf); + } + + PCPP_LOG_DEBUG("Pcap lib version info: " << IPcapDevice::getPcapLibVersionInfo()); + + pcap_if_t* currInterface = interfaceList; + while (currInterface != nullptr) + { +#if defined(_WIN32) + PcapLiveDevice* dev = new WinPcapLiveDevice(currInterface, true, true, true); +#else //__linux__, __APPLE__, __FreeBSD__ + PcapLiveDevice* dev = new PcapLiveDevice(currInterface, true, true, true); +#endif + currInterface = currInterface->next; + m_LiveDeviceList.insert(m_LiveDeviceList.end(), dev); + } + + setDnsServers(); + + PCPP_LOG_DEBUG("Freeing live device data"); + pcap_freealldevs(interfaceList); +} + +void PcapLiveDeviceList::setDnsServers() +{ +#if defined(_WIN32) + FIXED_INFO * fixedInfo; + ULONG ulOutBufLen; + DWORD dwRetVal; + IP_ADDR_STRING * pIPAddr; + + uint8_t buf1[sizeof(FIXED_INFO)]; + fixedInfo = (FIXED_INFO *) buf1; + ulOutBufLen = sizeof( FIXED_INFO ); + + dwRetVal = GetNetworkParams( fixedInfo, &ulOutBufLen ); + uint8_t* buf2 = new uint8_t[ulOutBufLen]; + if(ERROR_BUFFER_OVERFLOW == dwRetVal) + { + fixedInfo = (FIXED_INFO *)buf2; + } + + if ((dwRetVal = GetNetworkParams( fixedInfo, &ulOutBufLen )) != 0) + PCPP_LOG_ERROR("Call to GetNetworkParams failed. Return Value: " << std::hex << dwRetVal); + else + { + m_DnsServers.push_back(IPv4Address(fixedInfo->DnsServerList.IpAddress.String)); + int i = 1; + PCPP_LOG_DEBUG("Default DNS server IP #" << i++ << ": " << fixedInfo->DnsServerList.IpAddress.String); + + pIPAddr = fixedInfo->DnsServerList.Next; + while ( pIPAddr ) + { + m_DnsServers.push_back(IPv4Address(pIPAddr->IpAddress.String)); + PCPP_LOG_DEBUG("Default DNS server IP #" << i++ << ": " << pIPAddr->IpAddress.String); + pIPAddr = pIPAddr -> Next; + } + } + + delete[] buf2; +#elif defined(__linux__) + // verify that nmcli exist + std::string command = "command -v nmcli >/dev/null 2>&1 || { echo 'nmcli not installed'; }"; + std::string nmcliExists = executeShellCommand(command); + if (nmcliExists != "") + { + PCPP_LOG_DEBUG("Error retrieving DNS server list: nmcli doesn't exist"); + return; + } + + // check nmcli major version (0 or 1) + command = "nmcli -v | awk -F' ' '{print $NF}' | awk -F'.' '{print $1}'"; + std::string nmcliMajorVer = executeShellCommand(command); + nmcliMajorVer.erase(std::remove(nmcliMajorVer.begin(), nmcliMajorVer.end(), '\n'), nmcliMajorVer.end()); + PCPP_LOG_DEBUG("Found nmcli. nmcli major version is: '" << nmcliMajorVer << "'"); + + // build nmcli command according to its major version + if (nmcliMajorVer == "0") + command = "nmcli dev list | grep IP4.DNS"; + else + command = "nmcli dev show | grep IP4.DNS"; + + std::string dnsServersInfo = executeShellCommand(command); + if (dnsServersInfo == "") + { + PCPP_LOG_DEBUG("Error retrieving DNS server list: call to nmcli gave no output"); + return; + } + + std::istringstream stream(dnsServersInfo); + std::string line; + int i = 1; + while(std::getline(stream, line)) + { + std::istringstream lineStream(line); + std::string headline; + std::string dnsIP; + lineStream >> headline; + lineStream >> dnsIP; + IPv4Address dnsIPAddr(dnsIP); + if (!dnsIPAddr.isValid()) + continue; + + if (std::find(m_DnsServers.begin(), m_DnsServers.end(), dnsIPAddr) == m_DnsServers.end()) + { + m_DnsServers.push_back(dnsIPAddr); + PCPP_LOG_DEBUG("Default DNS server IP #" << i++ << ": " << dnsIPAddr); + } + } +#elif defined(__APPLE__) + + SCDynamicStoreRef dynRef = SCDynamicStoreCreate(kCFAllocatorSystemDefault, CFSTR("iked"), nullptr, nullptr); + if (dynRef == nullptr) + { + PCPP_LOG_DEBUG("Couldn't set DNS server list: failed to retrieve SCDynamicStore"); + return; + } + + CFDictionaryRef dnsDict = (CFDictionaryRef)SCDynamicStoreCopyValue(dynRef,CFSTR("State:/Network/Global/DNS")); + + if (dnsDict == nullptr) + { + PCPP_LOG_DEBUG("Couldn't set DNS server list: failed to get DNS dictionary"); + CFRelease(dynRef); + return; + } + + CFArrayRef serverAddresses = (CFArrayRef)CFDictionaryGetValue(dnsDict, CFSTR("ServerAddresses")); + + if (serverAddresses == nullptr) + { + PCPP_LOG_DEBUG("Couldn't set DNS server list: server addresses array is null"); + CFRelease(dynRef); + CFRelease(dnsDict); + return; + } + + CFIndex count = CFArrayGetCount(serverAddresses); + for (CFIndex i = 0; i < count; i++) + { + CFStringRef serverAddress = (CFStringRef)CFArrayGetValueAtIndex(serverAddresses, i); + + if (serverAddress == nullptr) + continue; + + uint8_t buf[20]; + char* serverAddressCString = (char*)buf; + CFStringGetCString(serverAddress, serverAddressCString, 20, kCFStringEncodingUTF8); + m_DnsServers.push_back(IPv4Address(serverAddressCString)); + PCPP_LOG_DEBUG("Default DNS server IP #" << (int)(i+1) << ": " << serverAddressCString); + } + + CFRelease(dynRef); + CFRelease(dnsDict); + +#elif defined(__FreeBSD__) + + res_init(); + + for (int i = 0; i < _res.nscount; i++) + { + sockaddr* saddr = (sockaddr*)&_res.nsaddr_list[i]; + if (saddr == NULL) + continue; + in_addr* inaddr = internal::sockaddr2in_addr(saddr); + if (inaddr == NULL) + continue; + m_DnsServers.push_back(IPv4Address(internal::in_addr2int(*inaddr))); + } + +#endif +} + +PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPAddress& ipAddr) const +{ + if (ipAddr.getType() == IPAddress::IPv4AddressType) + { + return getPcapLiveDeviceByIp(ipAddr.getIPv4()); + } + else //IPAddress::IPv6AddressType + { + return getPcapLiveDeviceByIp(ipAddr.getIPv6()); + } +} + +PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPv4Address& ipAddr) const +{ + PCPP_LOG_DEBUG("Searching all live devices..."); + for(std::vector::const_iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) + { + PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name << "'. Searching all addresses..."); + for(std::vector::iterator addrIter = (*devIter)->m_Addresses.begin(); addrIter != (*devIter)->m_Addresses.end(); addrIter++) + { + if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter->addr != nullptr) + { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(addrIter->addr, addrAsString); + PCPP_LOG_DEBUG("Searching address " << addrAsString); + } + + in_addr* currAddr = internal::sockaddr2in_addr(addrIter->addr); + if (currAddr == nullptr) + { + PCPP_LOG_DEBUG("Address is NULL"); + continue; + } + + if (currAddr->s_addr == ipAddr.toInt()) + { + PCPP_LOG_DEBUG("Found matched address!"); + return (*devIter); + } + } + } + + return nullptr; +} + +PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const IPv6Address& ip6Addr) const +{ + PCPP_LOG_DEBUG("Searching all live devices..."); + for(std::vector::const_iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) + { + PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name << "'. Searching all addresses..."); + for(std::vector::iterator addrIter = (*devIter)->m_Addresses.begin(); addrIter != (*devIter)->m_Addresses.end(); addrIter++) + { + if (Logger::getInstance().isDebugEnabled(PcapLogModuleLiveDevice) && addrIter->addr != nullptr) + { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(addrIter->addr, addrAsString); + PCPP_LOG_DEBUG("Searching address " << addrAsString); + } + + in6_addr* currAddr = internal::sockaddr2in6_addr(addrIter->addr); + if (currAddr == nullptr) + { + PCPP_LOG_DEBUG("Address is NULL"); + continue; + } + + uint8_t* addrAsArr; size_t addrLen; + ip6Addr.copyTo(&addrAsArr, addrLen); + if (memcmp(currAddr, addrAsArr, sizeof(struct in6_addr)) == 0) + { + PCPP_LOG_DEBUG("Found matched address!"); + delete [] addrAsArr; + return (*devIter); + } + + delete [] addrAsArr; + } + } + + return nullptr; +} + +PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIp(const std::string& ipAddrAsString) const +{ + IPAddress ipAddr(ipAddrAsString); + if (!ipAddr.isValid()) + { + PCPP_LOG_ERROR("IP address illegal"); + return nullptr; + } + + PcapLiveDevice* result = PcapLiveDeviceList::getPcapLiveDeviceByIp(ipAddr); + return result; +} + + +PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByName(const std::string& name) const +{ + PCPP_LOG_DEBUG("Searching all live devices..."); + for(std::vector::const_iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) + { + if (name == (*devIter)->getName()) + return (*devIter); + } + + return nullptr; +} + +PcapLiveDevice* PcapLiveDeviceList::getPcapLiveDeviceByIpOrName(const std::string& ipOrName) const +{ + IPAddress interfaceIP(ipOrName); + if (interfaceIP.isValid()) + { + return PcapLiveDeviceList::getInstance().getPcapLiveDeviceByIp(interfaceIP); + } + else + { + return PcapLiveDeviceList::getInstance().getPcapLiveDeviceByName(ipOrName); + } +} + +PcapLiveDeviceList* PcapLiveDeviceList::clone() +{ + return new PcapLiveDeviceList; +} + +void PcapLiveDeviceList::reset() +{ + for(std::vector::iterator devIter = m_LiveDeviceList.begin(); devIter != m_LiveDeviceList.end(); devIter++) + { + delete (*devIter); + } + + m_LiveDeviceList.clear(); + m_DnsServers.clear(); + + init(); +} + +} // namespace pcpp diff --git a/Pcap++/src/PcapRemoteDevice.cpp b/Pcap++/src/PcapRemoteDevice.cpp index 8e9b72f034..b08733f10c 100644 --- a/Pcap++/src/PcapRemoteDevice.cpp +++ b/Pcap++/src/PcapRemoteDevice.cpp @@ -1,140 +1,140 @@ -#if defined(_WIN32) - -#define LOG_MODULE PcapLogModuleRemoteDevice - -#include "PcapRemoteDevice.h" -#include "Logger.h" -#include "pcap.h" - - -namespace pcpp -{ - -pcap_rmtauth PcapRemoteAuthentication::getPcapRmAuth() const -{ - pcap_rmtauth result; - result.type = RPCAP_RMTAUTH_PWD; - result.username = (char*)userName.c_str(); - result.password = (char*)password.c_str(); - return result; -} - -PcapRemoteDevice::PcapRemoteDevice(pcap_if_t* iface, PcapRemoteAuthentication* remoteAuthentication, const IPAddress& remoteMachineIP, uint16_t remoteMachinePort) - : PcapLiveDevice(iface, false, false, false) -{ - PCPP_LOG_DEBUG("MTU calculation isn't supported for remote devices. Setting MTU to 1514"); - m_DeviceMtu = 1514; - m_RemoteMachineIpAddress = remoteMachineIP; - m_RemoteMachinePort = remoteMachinePort; - m_RemoteAuthentication = remoteAuthentication; -} - - -bool PcapRemoteDevice::open() -{ - char errbuf[PCAP_ERRBUF_SIZE]; - int flags = PCAP_OPENFLAG_PROMISCUOUS | PCAP_OPENFLAG_NOCAPTURE_RPCAP; //PCAP_OPENFLAG_DATATX_UDP doesn't always work - PCPP_LOG_DEBUG("Opening device '" << m_Name << "'"); - pcap_rmtauth* pRmAuth = NULL; - pcap_rmtauth rmAuth; - if (m_RemoteAuthentication != NULL) - { - rmAuth = m_RemoteAuthentication->getPcapRmAuth(); - pRmAuth = &rmAuth; - } - - m_PcapDescriptor = pcap_open(m_Name.c_str(), PCPP_MAX_PACKET_SIZE, flags, 250, pRmAuth, errbuf); - if (m_PcapDescriptor == NULL) - { - PCPP_LOG_ERROR("Error opening device. Error was: " << errbuf); - m_DeviceOpened = false; - return false; - } - - //in Remote devices there shouldn't be 2 separate descriptors - m_PcapSendDescriptor = m_PcapDescriptor; - - //setFilter requires that m_DeviceOpened == true - m_DeviceOpened = true; - - //for some reason if a filter is not set than WinPcap throws an exception. So Here is a generic filter that catches all traffic - if (!setFilter("ether proto (\\ip or \\ip6 or \\arp or \\rarp or \\decnet or \\sca or \\lat or \\mopdl or \\moprc or \\iso or \\stp or \\ipx or \\netbeui or 0x80F3)")) //0x80F3 == AARP - { - PCPP_LOG_ERROR("Error setting the filter. Error was: " << Logger::getInstance().getLastError()); - m_DeviceOpened = false; - return false; - } - - PCPP_LOG_DEBUG("Device '" << m_Name << "' opened"); - - return true; -} - -void* PcapRemoteDevice::remoteDeviceCaptureThreadMain(void *ptr) -{ - PcapRemoteDevice* pThis = (PcapRemoteDevice*)ptr; - if (pThis == NULL) - { - PCPP_LOG_ERROR("Capture thread: Unable to extract PcapLiveDevice instance"); - return 0; - } - - PCPP_LOG_DEBUG("Started capture thread for device '" << pThis->m_Name << "'"); - - pcap_pkthdr* pkthdr; - const uint8_t* pktData; - - if (pThis->m_CaptureCallbackMode) - { - while (!pThis->m_StopThread) - { - if (pcap_next_ex(pThis->m_PcapDescriptor, &pkthdr, &pktData) > 0) - onPacketArrives((uint8_t*)pThis, pkthdr, pktData); - } - } - else - { - while (!pThis->m_StopThread) - { - if (pcap_next_ex(pThis->m_PcapDescriptor, &pkthdr, &pktData) > 0) - onPacketArrivesNoCallback((uint8_t*)pThis, pkthdr, pktData); - } - } - PCPP_LOG_DEBUG("Ended capture thread for device '" << pThis->m_Name << "'"); - return 0; -} - -ThreadStart PcapRemoteDevice::getCaptureThreadStart() -{ - return &remoteDeviceCaptureThreadMain; -} - -void PcapRemoteDevice::getStatistics(PcapStats& stats) const -{ - int allocatedMemory; - pcap_stat* tempStats = pcap_stats_ex(m_PcapDescriptor, &allocatedMemory); - if (allocatedMemory < (int)sizeof(pcap_stat)) - { - PCPP_LOG_ERROR("Error getting statistics from live device '" << m_Name << "': WinPcap did not allocate the entire struct"); - return; - } - stats.packetsRecv = tempStats->ps_capt; - stats.packetsDrop = tempStats->ps_drop + tempStats->ps_netdrop; - stats.packetsDropByInterface = tempStats->ps_ifdrop; -} - -uint32_t PcapRemoteDevice::getMtu() const -{ - PCPP_LOG_DEBUG("MTU isn't supported for remote devices"); - return 0; -} - -MacAddress PcapRemoteDevice::getMacAddress() const -{ - PCPP_LOG_ERROR("MAC address isn't supported for remote devices"); - return MacAddress::Zero; -} - -} // namespace pcpp - -#endif // _WIN32 +#if defined(_WIN32) + +#define LOG_MODULE PcapLogModuleRemoteDevice + +#include "PcapRemoteDevice.h" +#include "Logger.h" +#include "pcap.h" + + +namespace pcpp +{ + +pcap_rmtauth PcapRemoteAuthentication::getPcapRmAuth() const +{ + pcap_rmtauth result; + result.type = RPCAP_RMTAUTH_PWD; + result.username = (char*)userName.c_str(); + result.password = (char*)password.c_str(); + return result; +} + +PcapRemoteDevice::PcapRemoteDevice(pcap_if_t* iface, PcapRemoteAuthentication* remoteAuthentication, const IPAddress& remoteMachineIP, uint16_t remoteMachinePort) + : PcapLiveDevice(iface, false, false, false) +{ + PCPP_LOG_DEBUG("MTU calculation isn't supported for remote devices. Setting MTU to 1514"); + m_DeviceMtu = 1514; + m_RemoteMachineIpAddress = remoteMachineIP; + m_RemoteMachinePort = remoteMachinePort; + m_RemoteAuthentication = remoteAuthentication; +} + + +bool PcapRemoteDevice::open() +{ + char errbuf[PCAP_ERRBUF_SIZE]; + int flags = PCAP_OPENFLAG_PROMISCUOUS | PCAP_OPENFLAG_NOCAPTURE_RPCAP; //PCAP_OPENFLAG_DATATX_UDP doesn't always work + PCPP_LOG_DEBUG("Opening device '" << m_Name << "'"); + pcap_rmtauth* pRmAuth = NULL; + pcap_rmtauth rmAuth; + if (m_RemoteAuthentication != NULL) + { + rmAuth = m_RemoteAuthentication->getPcapRmAuth(); + pRmAuth = &rmAuth; + } + + m_PcapDescriptor = pcap_open(m_Name.c_str(), PCPP_MAX_PACKET_SIZE, flags, 250, pRmAuth, errbuf); + if (m_PcapDescriptor == NULL) + { + PCPP_LOG_ERROR("Error opening device. Error was: " << errbuf); + m_DeviceOpened = false; + return false; + } + + //in Remote devices there shouldn't be 2 separate descriptors + m_PcapSendDescriptor = m_PcapDescriptor; + + //setFilter requires that m_DeviceOpened == true + m_DeviceOpened = true; + + //for some reason if a filter is not set than WinPcap throws an exception. So Here is a generic filter that catches all traffic + if (!setFilter("ether proto (\\ip or \\ip6 or \\arp or \\rarp or \\decnet or \\sca or \\lat or \\mopdl or \\moprc or \\iso or \\stp or \\ipx or \\netbeui or 0x80F3)")) //0x80F3 == AARP + { + PCPP_LOG_ERROR("Error setting the filter. Error was: " << Logger::getInstance().getLastError()); + m_DeviceOpened = false; + return false; + } + + PCPP_LOG_DEBUG("Device '" << m_Name << "' opened"); + + return true; +} + +void* PcapRemoteDevice::remoteDeviceCaptureThreadMain(void *ptr) +{ + PcapRemoteDevice* pThis = (PcapRemoteDevice*)ptr; + if (pThis == NULL) + { + PCPP_LOG_ERROR("Capture thread: Unable to extract PcapLiveDevice instance"); + return 0; + } + + PCPP_LOG_DEBUG("Started capture thread for device '" << pThis->m_Name << "'"); + + pcap_pkthdr* pkthdr; + const uint8_t* pktData; + + if (pThis->m_CaptureCallbackMode) + { + while (!pThis->m_StopThread) + { + if (pcap_next_ex(pThis->m_PcapDescriptor, &pkthdr, &pktData) > 0) + onPacketArrives((uint8_t*)pThis, pkthdr, pktData); + } + } + else + { + while (!pThis->m_StopThread) + { + if (pcap_next_ex(pThis->m_PcapDescriptor, &pkthdr, &pktData) > 0) + onPacketArrivesNoCallback((uint8_t*)pThis, pkthdr, pktData); + } + } + PCPP_LOG_DEBUG("Ended capture thread for device '" << pThis->m_Name << "'"); + return 0; +} + +ThreadStart PcapRemoteDevice::getCaptureThreadStart() +{ + return &remoteDeviceCaptureThreadMain; +} + +void PcapRemoteDevice::getStatistics(PcapStats& stats) const +{ + int allocatedMemory; + pcap_stat* tempStats = pcap_stats_ex(m_PcapDescriptor, &allocatedMemory); + if (allocatedMemory < (int)sizeof(pcap_stat)) + { + PCPP_LOG_ERROR("Error getting statistics from live device '" << m_Name << "': WinPcap did not allocate the entire struct"); + return; + } + stats.packetsRecv = tempStats->ps_capt; + stats.packetsDrop = tempStats->ps_drop + tempStats->ps_netdrop; + stats.packetsDropByInterface = tempStats->ps_ifdrop; +} + +uint32_t PcapRemoteDevice::getMtu() const +{ + PCPP_LOG_DEBUG("MTU isn't supported for remote devices"); + return 0; +} + +MacAddress PcapRemoteDevice::getMacAddress() const +{ + PCPP_LOG_ERROR("MAC address isn't supported for remote devices"); + return MacAddress::Zero; +} + +} // namespace pcpp + +#endif // _WIN32 diff --git a/Pcap++/src/PcapRemoteDeviceList.cpp b/Pcap++/src/PcapRemoteDeviceList.cpp index b38d3759b4..94197d3513 100644 --- a/Pcap++/src/PcapRemoteDeviceList.cpp +++ b/Pcap++/src/PcapRemoteDeviceList.cpp @@ -1,223 +1,223 @@ -#if defined(_WIN32) - -#define LOG_MODULE PcapLogModuleRemoteDevice - -#include "PcapRemoteDeviceList.h" -#include "Logger.h" -#include "IpUtils.h" -#include "pcap.h" -#include - -namespace pcpp -{ - -PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port) -{ - return PcapRemoteDeviceList::getRemoteDeviceList(ipAddress, port, NULL); -} - -PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port, PcapRemoteAuthentication* remoteAuth) -{ - if (!ipAddress.isValid()) - { - PCPP_LOG_ERROR("IP address is NULL or not valid"); - return NULL; - } - - PCPP_LOG_DEBUG("Searching remote devices on IP: " << ipAddress << " and port: " << port); - char remoteCaptureString[PCAP_BUF_SIZE]; - char errbuf[PCAP_ERRBUF_SIZE]; - std::ostringstream portAsString; - portAsString << port; - if (pcap_createsrcstr(remoteCaptureString, PCAP_SRC_IFREMOTE, ipAddress.toString().c_str(), portAsString.str().c_str(), NULL, errbuf) != 0) - { - PCPP_LOG_ERROR("Error in creating the remote connection string. Error was: " << errbuf); - return NULL; - } - - PCPP_LOG_DEBUG("Remote capture string: " << remoteCaptureString); - - pcap_rmtauth* pRmAuth = NULL; - pcap_rmtauth rmAuth; - if (remoteAuth != NULL) - { - PCPP_LOG_DEBUG("Authentication requested. Username: " << remoteAuth->userName << ", Password: " << remoteAuth->password); - rmAuth = remoteAuth->getPcapRmAuth(); - pRmAuth = &rmAuth; - } - - pcap_if_t* interfaceList; - char errorBuf[PCAP_ERRBUF_SIZE]; - if (pcap_findalldevs_ex(remoteCaptureString, pRmAuth, &interfaceList, errorBuf) < 0) - { - PCPP_LOG_ERROR("Error retrieving device on remote machine. Error string is: " << errorBuf); - return NULL; - } - - PcapRemoteDeviceList* resultList = new PcapRemoteDeviceList(); - resultList->setRemoteMachineIpAddress(ipAddress); - resultList->setRemoteMachinePort(port); - resultList->setRemoteAuthentication(remoteAuth); - - pcap_if_t* currInterface = interfaceList; - while (currInterface != NULL) - { - PcapRemoteDevice* pNewRemoteDevice = new PcapRemoteDevice(currInterface, resultList->m_RemoteAuthentication, - resultList->getRemoteMachineIpAddress(), resultList->getRemoteMachinePort()); - resultList->m_RemoteDeviceList.push_back(pNewRemoteDevice); - currInterface = currInterface->next; - } - - pcap_freealldevs(interfaceList); - return resultList; -} - -PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const std::string& ipAddrAsString) const -{ - IPAddress ipAddr = IPAddress(ipAddrAsString); - if (!ipAddr.isValid()) - { - PCPP_LOG_ERROR("IP address no valid"); - return NULL; - } - - PcapRemoteDevice* result = getRemoteDeviceByIP(ipAddr); - return result; -} - -PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const IPAddress& ipAddr) const -{ - if (!ipAddr.isValid()) - { - PCPP_LOG_ERROR("IP address not valid"); - return NULL; - } - if (ipAddr.getType() == IPAddress::IPv4AddressType) - { - return getRemoteDeviceByIP(ipAddr.getIPv4()); - } - else //IPAddress::IPv6AddressType - { - return getRemoteDeviceByIP(ipAddr.getIPv6()); - } -} - - -PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const IPv4Address& ip4Addr) const -{ - PCPP_LOG_DEBUG("Searching all remote devices in list..."); - for(ConstRemoteDeviceListIterator devIter = m_RemoteDeviceList.begin(); devIter != m_RemoteDeviceList.end(); devIter++) - { - PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name << "'. Searching all addresses..."); - for(std::vector::iterator addrIter = (*devIter)->m_Addresses.begin(); addrIter != (*devIter)->m_Addresses.end(); addrIter++) - { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleRemoteDevice) && addrIter->addr != NULL) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter->addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); - } - - in_addr* currAddr = internal::sockaddr2in_addr(addrIter->addr); - if (currAddr == NULL) - { - PCPP_LOG_DEBUG("Address is NULL"); - continue; - } - - if (currAddr->s_addr == ip4Addr.toInt()) - { - PCPP_LOG_DEBUG("Found matched address!"); - return (*devIter); - } - } - } - - return NULL; - -} - -PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const IPv6Address& ip6Addr) const -{ - PCPP_LOG_DEBUG("Searching all remote devices in list..."); - for(ConstRemoteDeviceListIterator devIter = m_RemoteDeviceList.begin(); devIter != m_RemoteDeviceList.end(); devIter++) - { - PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name << "'. Searching all addresses..."); - for(std::vector::iterator addrIter = (*devIter)->m_Addresses.begin(); addrIter != (*devIter)->m_Addresses.end(); addrIter++) - { - if (Logger::getInstance().isDebugEnabled(PcapLogModuleRemoteDevice) && addrIter->addr != NULL) - { - char addrAsString[INET6_ADDRSTRLEN]; - internal::sockaddr2string(addrIter->addr, addrAsString); - PCPP_LOG_DEBUG("Searching address " << addrAsString); - } - - in6_addr* currAddr = internal::sockaddr2in6_addr(addrIter->addr); - if (currAddr == NULL) - { - PCPP_LOG_DEBUG("Address is NULL"); - continue; - } - - uint8_t* addrAsArr; size_t addrLen; - ip6Addr.copyTo(&addrAsArr, addrLen); - if (memcmp(currAddr, addrAsArr, sizeof(struct in6_addr)) == 0) - { - PCPP_LOG_DEBUG("Found matched address!"); - delete [] addrAsArr; - return (*devIter); - } - delete [] addrAsArr; - } - } - - return NULL; - -} - -void PcapRemoteDeviceList::setRemoteMachineIpAddress(const IPAddress& ipAddress) -{ - if (!ipAddress.isValid()) - { - PCPP_LOG_ERROR("Trying to set an invalid IP address to PcapRemoteDeviceList"); - return; - } - - m_RemoteMachineIpAddress = ipAddress; -} - -void PcapRemoteDeviceList::setRemoteMachinePort(uint16_t port) -{ - m_RemoteMachinePort = port; -} - -void PcapRemoteDeviceList::setRemoteAuthentication(const PcapRemoteAuthentication* remoteAuth) -{ - if (remoteAuth != NULL) - m_RemoteAuthentication = new PcapRemoteAuthentication(*remoteAuth); - else - { - if (m_RemoteAuthentication != NULL) - delete m_RemoteAuthentication; - m_RemoteAuthentication = NULL; - } -} - -PcapRemoteDeviceList::~PcapRemoteDeviceList() -{ - while (m_RemoteDeviceList.size() > 0) - { - RemoteDeviceListIterator devIter = m_RemoteDeviceList.begin(); - delete (*devIter); - m_RemoteDeviceList.erase(devIter); - } - - if (m_RemoteAuthentication != NULL) - { - delete m_RemoteAuthentication; - } -} - -} // namespace pcpp - -#endif // _WIN32 +#if defined(_WIN32) + +#define LOG_MODULE PcapLogModuleRemoteDevice + +#include "PcapRemoteDeviceList.h" +#include "Logger.h" +#include "IpUtils.h" +#include "pcap.h" +#include + +namespace pcpp +{ + +PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port) +{ + return PcapRemoteDeviceList::getRemoteDeviceList(ipAddress, port, NULL); +} + +PcapRemoteDeviceList* PcapRemoteDeviceList::getRemoteDeviceList(const IPAddress& ipAddress, uint16_t port, PcapRemoteAuthentication* remoteAuth) +{ + if (!ipAddress.isValid()) + { + PCPP_LOG_ERROR("IP address is NULL or not valid"); + return NULL; + } + + PCPP_LOG_DEBUG("Searching remote devices on IP: " << ipAddress << " and port: " << port); + char remoteCaptureString[PCAP_BUF_SIZE]; + char errbuf[PCAP_ERRBUF_SIZE]; + std::ostringstream portAsString; + portAsString << port; + if (pcap_createsrcstr(remoteCaptureString, PCAP_SRC_IFREMOTE, ipAddress.toString().c_str(), portAsString.str().c_str(), NULL, errbuf) != 0) + { + PCPP_LOG_ERROR("Error in creating the remote connection string. Error was: " << errbuf); + return NULL; + } + + PCPP_LOG_DEBUG("Remote capture string: " << remoteCaptureString); + + pcap_rmtauth* pRmAuth = NULL; + pcap_rmtauth rmAuth; + if (remoteAuth != NULL) + { + PCPP_LOG_DEBUG("Authentication requested. Username: " << remoteAuth->userName << ", Password: " << remoteAuth->password); + rmAuth = remoteAuth->getPcapRmAuth(); + pRmAuth = &rmAuth; + } + + pcap_if_t* interfaceList; + char errorBuf[PCAP_ERRBUF_SIZE]; + if (pcap_findalldevs_ex(remoteCaptureString, pRmAuth, &interfaceList, errorBuf) < 0) + { + PCPP_LOG_ERROR("Error retrieving device on remote machine. Error string is: " << errorBuf); + return NULL; + } + + PcapRemoteDeviceList* resultList = new PcapRemoteDeviceList(); + resultList->setRemoteMachineIpAddress(ipAddress); + resultList->setRemoteMachinePort(port); + resultList->setRemoteAuthentication(remoteAuth); + + pcap_if_t* currInterface = interfaceList; + while (currInterface != NULL) + { + PcapRemoteDevice* pNewRemoteDevice = new PcapRemoteDevice(currInterface, resultList->m_RemoteAuthentication, + resultList->getRemoteMachineIpAddress(), resultList->getRemoteMachinePort()); + resultList->m_RemoteDeviceList.push_back(pNewRemoteDevice); + currInterface = currInterface->next; + } + + pcap_freealldevs(interfaceList); + return resultList; +} + +PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const std::string& ipAddrAsString) const +{ + IPAddress ipAddr = IPAddress(ipAddrAsString); + if (!ipAddr.isValid()) + { + PCPP_LOG_ERROR("IP address no valid"); + return NULL; + } + + PcapRemoteDevice* result = getRemoteDeviceByIP(ipAddr); + return result; +} + +PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const IPAddress& ipAddr) const +{ + if (!ipAddr.isValid()) + { + PCPP_LOG_ERROR("IP address not valid"); + return NULL; + } + if (ipAddr.getType() == IPAddress::IPv4AddressType) + { + return getRemoteDeviceByIP(ipAddr.getIPv4()); + } + else //IPAddress::IPv6AddressType + { + return getRemoteDeviceByIP(ipAddr.getIPv6()); + } +} + + +PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const IPv4Address& ip4Addr) const +{ + PCPP_LOG_DEBUG("Searching all remote devices in list..."); + for(ConstRemoteDeviceListIterator devIter = m_RemoteDeviceList.begin(); devIter != m_RemoteDeviceList.end(); devIter++) + { + PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name << "'. Searching all addresses..."); + for(std::vector::iterator addrIter = (*devIter)->m_Addresses.begin(); addrIter != (*devIter)->m_Addresses.end(); addrIter++) + { + if (Logger::getInstance().isDebugEnabled(PcapLogModuleRemoteDevice) && addrIter->addr != NULL) + { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(addrIter->addr, addrAsString); + PCPP_LOG_DEBUG("Searching address " << addrAsString); + } + + in_addr* currAddr = internal::sockaddr2in_addr(addrIter->addr); + if (currAddr == NULL) + { + PCPP_LOG_DEBUG("Address is NULL"); + continue; + } + + if (currAddr->s_addr == ip4Addr.toInt()) + { + PCPP_LOG_DEBUG("Found matched address!"); + return (*devIter); + } + } + } + + return NULL; + +} + +PcapRemoteDevice* PcapRemoteDeviceList::getRemoteDeviceByIP(const IPv6Address& ip6Addr) const +{ + PCPP_LOG_DEBUG("Searching all remote devices in list..."); + for(ConstRemoteDeviceListIterator devIter = m_RemoteDeviceList.begin(); devIter != m_RemoteDeviceList.end(); devIter++) + { + PCPP_LOG_DEBUG("Searching device '" << (*devIter)->m_Name << "'. Searching all addresses..."); + for(std::vector::iterator addrIter = (*devIter)->m_Addresses.begin(); addrIter != (*devIter)->m_Addresses.end(); addrIter++) + { + if (Logger::getInstance().isDebugEnabled(PcapLogModuleRemoteDevice) && addrIter->addr != NULL) + { + char addrAsString[INET6_ADDRSTRLEN]; + internal::sockaddr2string(addrIter->addr, addrAsString); + PCPP_LOG_DEBUG("Searching address " << addrAsString); + } + + in6_addr* currAddr = internal::sockaddr2in6_addr(addrIter->addr); + if (currAddr == NULL) + { + PCPP_LOG_DEBUG("Address is NULL"); + continue; + } + + uint8_t* addrAsArr; size_t addrLen; + ip6Addr.copyTo(&addrAsArr, addrLen); + if (memcmp(currAddr, addrAsArr, sizeof(struct in6_addr)) == 0) + { + PCPP_LOG_DEBUG("Found matched address!"); + delete [] addrAsArr; + return (*devIter); + } + delete [] addrAsArr; + } + } + + return NULL; + +} + +void PcapRemoteDeviceList::setRemoteMachineIpAddress(const IPAddress& ipAddress) +{ + if (!ipAddress.isValid()) + { + PCPP_LOG_ERROR("Trying to set an invalid IP address to PcapRemoteDeviceList"); + return; + } + + m_RemoteMachineIpAddress = ipAddress; +} + +void PcapRemoteDeviceList::setRemoteMachinePort(uint16_t port) +{ + m_RemoteMachinePort = port; +} + +void PcapRemoteDeviceList::setRemoteAuthentication(const PcapRemoteAuthentication* remoteAuth) +{ + if (remoteAuth != NULL) + m_RemoteAuthentication = new PcapRemoteAuthentication(*remoteAuth); + else + { + if (m_RemoteAuthentication != NULL) + delete m_RemoteAuthentication; + m_RemoteAuthentication = NULL; + } +} + +PcapRemoteDeviceList::~PcapRemoteDeviceList() +{ + while (m_RemoteDeviceList.size() > 0) + { + RemoteDeviceListIterator devIter = m_RemoteDeviceList.begin(); + delete (*devIter); + m_RemoteDeviceList.erase(devIter); + } + + if (m_RemoteAuthentication != NULL) + { + delete m_RemoteAuthentication; + } +} + +} // namespace pcpp + +#endif // _WIN32 diff --git a/Pcap++/src/PfRingDevice.cpp b/Pcap++/src/PfRingDevice.cpp index 852edc7a29..9a07f0c965 100644 --- a/Pcap++/src/PfRingDevice.cpp +++ b/Pcap++/src/PfRingDevice.cpp @@ -1,887 +1,887 @@ -#ifdef USE_PF_RING - -#define LOG_MODULE PcapLogModulePfRingDevice - -#include "PfRingDevice.h" -#include "EthLayer.h" -#include "VlanLayer.h" -#include "Logger.h" -#include -#include -#include -#include - - -#define DEFAULT_PF_RING_SNAPLEN 1600 - -namespace pcpp -{ - - -PfRingDevice::PfRingDevice(const char* deviceName) : m_MacAddress(MacAddress::Zero) -{ - m_NumOfOpenedRxChannels = 0; - m_DeviceOpened = false; - m_DeviceName = std::string(deviceName); - m_InterfaceIndex = -1; - m_StopThread = true; - m_OnPacketsArriveCallback = NULL; - m_OnPacketsArriveUserCookie = NULL; - m_ReentrantMode = false; - m_HwClockEnabled = false; - m_DeviceMTU = 0; - m_IsFilterCurrentlySet = false; - - m_PfRingDescriptors = new pfring*[MAX_NUM_RX_CHANNELS]; -} - -PfRingDevice::~PfRingDevice() -{ - close(); - delete [] m_PfRingDescriptors; -} - - -bool PfRingDevice::open() -{ - if (m_DeviceOpened) - { - PCPP_LOG_ERROR("Device already opened"); - return false; - } - - m_NumOfOpenedRxChannels = 0; - - PCPP_LOG_DEBUG("Trying to open device [" << m_DeviceName << "]"); - int res = openSingleRxChannel(m_DeviceName.c_str(), &m_PfRingDescriptors[0]); - if (res == 0) - { - PCPP_LOG_DEBUG("Succeeded opening device [" << m_DeviceName << "]"); - m_NumOfOpenedRxChannels = 1; - m_DeviceOpened = true; - return true; - } - else if (res == 1) - PCPP_LOG_ERROR("Couldn't open a ring on device [" << m_DeviceName << "]"); - else if (res == 2) - PCPP_LOG_ERROR("Unable to enable ring for device [" << m_DeviceName << "]"); - - return false; -} - - -bool PfRingDevice::openSingleRxChannel(uint8_t channelId) -{ - uint8_t channelIds[1] = { channelId }; - return openMultiRxChannels(channelIds, 1); -} - -int PfRingDevice::openSingleRxChannel(const char* deviceName, pfring** ring) -{ - if (m_DeviceOpened) - { - PCPP_LOG_ERROR("Device already opened"); - return false; - } - - uint32_t flags = PF_RING_PROMISC | PF_RING_HW_TIMESTAMP | PF_RING_DNA_SYMMETRIC_RSS; - *ring = pfring_open(deviceName, DEFAULT_PF_RING_SNAPLEN, flags); - - if (*ring == NULL) - { - return 1; - } - PCPP_LOG_DEBUG("pfring_open Succeeded for device [" << m_DeviceName << "]"); - - if (getIsHwClockEnable()) - { - setPfRingDeviceClock(*ring); - PCPP_LOG_DEBUG("H/W clock set for device [" << m_DeviceName << "]"); - } - - if (pfring_enable_rss_rehash(*ring) < 0 || pfring_enable_ring(*ring) < 0) - { - pfring_close(*ring); - return 2; - } - - PCPP_LOG_DEBUG("pfring enabled for device [" << m_DeviceName << "]"); - - return 0; -} - -bool PfRingDevice::setPfRingDeviceClock(pfring* ring) -{ - struct timespec ltime; - if (clock_gettime(CLOCK_REALTIME, <ime) != 0) - { - PCPP_LOG_ERROR("Could not set pfring devices clock, clock_gettime failed"); - return false; - } - - if (pfring_set_device_clock(ring, <ime) < 0) - { - PCPP_LOG_DEBUG("Could not set pfring devices clock, pfring_set_device_clock failed"); - return false; - } - - return true; -} - -bool PfRingDevice::openMultiRxChannels(const uint8_t* channelIds, int numOfChannelIds) -{ - if (m_DeviceOpened) - { - PCPP_LOG_ERROR("Device already opened"); - return false; - } - - // I needed to add this verification because PF_RING doesn't provide it. - // It allows opening the device on a channel that doesn't exist, but of course no packets will be captured - uint8_t totalChannels = getTotalNumOfRxChannels(); - for (int i = 0; i < numOfChannelIds; i++) - { - uint8_t channelId = channelIds[i]; - if (channelId >= totalChannels) - { - PCPP_LOG_ERROR("Trying to open the device with a RX channel that doesn't exist. Total RX channels are [" << (int)totalChannels << "], tried to open channel [" << (int)channelId << "]"); - return false; - } - } - - m_NumOfOpenedRxChannels = 0; - - for (int i = 0; i < numOfChannelIds; i++) - { - uint8_t channelId = channelIds[i]; - std::ostringstream ringNameStream; - ringNameStream << m_DeviceName << "@" << (int)channelId; - std::string ringName = ringNameStream.str(); - PCPP_LOG_DEBUG("Trying to open device [" << m_DeviceName << "] on channel [" << channelId << "]. Channel name [" << ringName << "]"); - int res = openSingleRxChannel(ringName.c_str(), &m_PfRingDescriptors[i]); - if (res == 0) - { - PCPP_LOG_DEBUG("Succeeded opening device [" << m_DeviceName << "] on channel [" << channelId << "]. Channel name [" << ringName << "]"); - m_NumOfOpenedRxChannels++; - continue; - } - else if (res == 1) - PCPP_LOG_ERROR("Couldn't open a ring on channel [" << (int)channelId << "] for device [" << m_DeviceName << "]"); - else if (res == 2) - PCPP_LOG_ERROR("Unable to enable ring on channel [" << (int)channelId << "] for device [" << m_DeviceName << "]"); - - break; - } - - if (m_NumOfOpenedRxChannels < numOfChannelIds) - { - // if an error occurred, close all rings from index=0 to index=m_NumOfOpenedRxChannels-1 - // there's no need to close m_PfRingDescriptors[m_NumOfOpenedRxChannels] because it has already been - // closed by openSingleRxChannel - for (int i = 0; i < m_NumOfOpenedRxChannels-1; i++) - { - pfring_close(m_PfRingDescriptors[i]); - } - - m_NumOfOpenedRxChannels = 0; - return false; - } - - m_DeviceOpened = true; - - return true; -} - -bool PfRingDevice::openMultiRxChannels(uint8_t numOfRxChannelsToOpen, ChannelDistribution dist) -{ - if (m_DeviceOpened) - { - PCPP_LOG_ERROR("Device already opened"); - return false; - } - - m_NumOfOpenedRxChannels = 0; - - if (numOfRxChannelsToOpen > MAX_NUM_RX_CHANNELS) - { - PCPP_LOG_ERROR("Cannot open more than [" << MAX_NUM_RX_CHANNELS << "] channels"); - return false; - } - - uint32_t flags = PF_RING_PROMISC | PF_RING_REENTRANT | PF_RING_HW_TIMESTAMP | PF_RING_DNA_SYMMETRIC_RSS; - - uint8_t numOfRxChannelsOnNIC = getTotalNumOfRxChannels(); - PCPP_LOG_DEBUG("NIC has " << (int)numOfRxChannelsOnNIC << " RX channels"); - - uint8_t numOfRingsPerRxChannel = numOfRxChannelsToOpen / numOfRxChannelsOnNIC; - uint8_t remainderRings = numOfRxChannelsToOpen % numOfRxChannelsOnNIC; - - cluster_type clusterType = (dist == RoundRobin) ? cluster_round_robin : cluster_per_flow; - - int ringsOpen = 0; - for (uint8_t channelId = 0; channelId < numOfRxChannelsOnNIC; channelId++) - { - // no more channels to open - if (numOfRingsPerRxChannel == 0 && remainderRings == 0) - break; - - std::ostringstream ringName; - ringName << m_DeviceName << "@" << (int)channelId; - - // open numOfRingsPerRxChannel rings per RX channel - for (uint8_t ringId = 0; ringId < numOfRingsPerRxChannel; ringId++) - { - m_PfRingDescriptors[ringsOpen] = pfring_open(ringName.str().c_str(), DEFAULT_PF_RING_SNAPLEN, flags); - if (m_PfRingDescriptors[ringsOpen] == NULL) - { - PCPP_LOG_ERROR("Couldn't open a ring on channel [" << (int)channelId << "]"); - break; - } - - // setting a cluster for all rings in the same channel to enable hashing between them - if (pfring_set_cluster(m_PfRingDescriptors[ringsOpen], channelId+1, clusterType) < 0) - { - PCPP_LOG_ERROR("Couldn't set ring [" << (int)ringId << "] in channel [" << (int)channelId << "] to the cluster [" << (int)(channelId+1) << "]"); - break; - } - - ringsOpen++; - } - - // open one more ring if remainder > 0 - if (remainderRings > 0) - { - m_PfRingDescriptors[ringsOpen] = pfring_open(ringName.str().c_str(), DEFAULT_PF_RING_SNAPLEN, flags); - if (m_PfRingDescriptors[ringsOpen] == NULL) - { - PCPP_LOG_ERROR("Couldn't open a ring on channel [" << (int)channelId << "]"); - break; - } - - // setting a cluster for all rings in the same channel to enable hashing between them - if (pfring_set_cluster(m_PfRingDescriptors[ringsOpen], channelId+1, clusterType) < 0) - { - PCPP_LOG_ERROR("Couldn't set ring [" << (int)(numOfRingsPerRxChannel+1) << "] in channel [" << (int)channelId << "] to the cluster [" << (int)(channelId+1) << "]"); - break; - } - - ringsOpen++; - remainderRings--; - PCPP_LOG_DEBUG("Opened " << (int)(numOfRingsPerRxChannel+1) << " rings on channel [" << (int)channelId << "]"); - } - else - PCPP_LOG_DEBUG("Opened " << (int)numOfRingsPerRxChannel << " rings on channel [" << (int)channelId << "]"); - } - - if (ringsOpen < numOfRxChannelsToOpen) - { - for (uint8_t i = 0; i < ringsOpen; i++) - pfring_close(m_PfRingDescriptors[i]); - return false; - } - - if (getIsHwClockEnable()) - { - for (int i = 0; i < ringsOpen; i++) - { - if (setPfRingDeviceClock(m_PfRingDescriptors[i])) - PCPP_LOG_DEBUG("H/W clock set for device [" << m_DeviceName << "]"); - } - } - - // enable all rings - for (int i = 0; i < ringsOpen; i++) - { - if (pfring_enable_rss_rehash(m_PfRingDescriptors[i]) < 0 || pfring_enable_ring(m_PfRingDescriptors[i]) < 0) - { - PCPP_LOG_ERROR("Unable to enable ring [" << i << "] for device [" << m_DeviceName << "]"); - // close all pfring's that were enabled until now - for (int j = 0; j 0) - { - uint8_t res = pfring_get_num_rx_channels(m_PfRingDescriptors[0]); - return res; - } - else - { - uint32_t flags = PF_RING_PROMISC | PF_RING_REENTRANT | PF_RING_HW_TIMESTAMP | PF_RING_DNA_SYMMETRIC_RSS; - pfring* ring = pfring_open(m_DeviceName.c_str(), DEFAULT_PF_RING_SNAPLEN, flags); - uint8_t res = pfring_get_num_rx_channels(ring); - pfring_close(ring); - return res; - } -} - - -SystemCore PfRingDevice::getCurrentCoreId() const -{ - return SystemCores::IdToSystemCore[sched_getcpu()]; -} - - -bool PfRingDevice::setFilter(std::string filterAsString) -{ - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device not opened"); - return false; - } - - for (int i = 0; i < m_NumOfOpenedRxChannels; i++) - { - int res = pfring_set_bpf_filter(m_PfRingDescriptors[i], (char*)filterAsString.c_str()); - if(res < 0) - { - if (res == PF_RING_ERROR_NOT_SUPPORTED) - PCPP_LOG_ERROR("BPF filtering isn't supported on current PF_RING version. Please re-compile PF_RING with the --enable-bpf flag"); - else - PCPP_LOG_ERROR("Couldn't set filter '" << filterAsString << "'"); - return false; - } - } - - m_IsFilterCurrentlySet = true; - - PCPP_LOG_DEBUG("Successfully set filter '" << filterAsString << "'"); - return true; -} - - -bool PfRingDevice::clearFilter() -{ - if (!m_IsFilterCurrentlySet) - return true; - - for (int i = 0; i < m_NumOfOpenedRxChannels; i++) - { - int res = pfring_remove_bpf_filter(m_PfRingDescriptors[i]); - if(res < 0) - { - PCPP_LOG_ERROR("Couldn't remove filter"); - return false; - } - } - - m_IsFilterCurrentlySet = false; - - PCPP_LOG_DEBUG("Successfully removed filter from all open RX channels"); - return true; -} - - -bool PfRingDevice::isFilterCurrentlySet() const -{ - return m_IsFilterCurrentlySet; -} - - -void PfRingDevice::close() -{ - for (int i = 0; i < m_NumOfOpenedRxChannels; i++) - pfring_close(m_PfRingDescriptors[i]); - m_DeviceOpened = false; - clearCoreConfiguration(); - m_NumOfOpenedRxChannels = 0; - m_IsFilterCurrentlySet = false; - PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] closed"); -} - -bool PfRingDevice::initCoreConfigurationByCoreMask(CoreMask coreMask) -{ - int i = 0; - int numOfCores = getNumOfCores(); - clearCoreConfiguration(); - while ((coreMask != 0) && (i < numOfCores)) - { - if (coreMask & 1) - { - m_CoreConfiguration[i].IsInUse = true; - } - - coreMask = coreMask >> 1; - i++; - } - - if (coreMask != 0) // this mean coreMask contains a core that doesn't exist - { - PCPP_LOG_ERROR("Trying to use a core [" << i << "] that doesn't exist while machine has " << numOfCores << " cores"); - clearCoreConfiguration(); - return false; - } - - return true; -} - -bool PfRingDevice::startCaptureMultiThread(OnPfRingPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie, CoreMask coreMask) -{ - if (!m_StopThread) - { - PCPP_LOG_ERROR("Device already capturing. Cannot start 2 capture sessions at the same time"); - return false; - } - - if (!initCoreConfigurationByCoreMask(coreMask)) - return false; - - if (m_NumOfOpenedRxChannels != getCoresInUseCount()) - { - PCPP_LOG_ERROR("Cannot use a different number of channels and cores. Opened " << m_NumOfOpenedRxChannels << " channels but set " << getCoresInUseCount() << " cores in core mask"); - clearCoreConfiguration(); - return false; - } - - std::mutex mutex; - std::condition_variable cond; - int startThread = 0; - - m_StopThread = false; - int rxChannel = 0; - for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) - { - if (!m_CoreConfiguration[coreId].IsInUse) - continue; - - m_ReentrantMode = true; - - m_OnPacketsArriveCallback = onPacketsArrive; - m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; - - // create a new thread - m_CoreConfiguration[coreId].Channel = m_PfRingDescriptors[rxChannel++]; - m_CoreConfiguration[coreId].RxThread = std::thread(&pcpp::PfRingDevice::captureThreadMain, this, &cond, &mutex, &startThread); - - // set affinity to cores - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(coreId, &cpuset); - int err = pthread_setaffinity_np(m_CoreConfiguration[coreId].RxThread.native_handle(), sizeof(cpu_set_t), &cpuset); - if(err != 0) - { - PCPP_LOG_ERROR("Error while binding thread to core " << coreId << ": errno=" << err); - startThread = 1; - clearCoreConfiguration(); - return false; - } - } - - startThread = 2; - cond.notify_all(); - - return true; -} - -bool PfRingDevice::startCaptureSingleThread(OnPfRingPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie) -{ - if (!m_StopThread) - { - PCPP_LOG_ERROR("Device already capturing. Cannot start 2 capture sessions at the same time"); - return false; - } - - if (m_NumOfOpenedRxChannels != 1) - { - PCPP_LOG_ERROR("Cannot start capturing on a single thread when more than 1 RX channel is opened"); - return false; - } - - PCPP_LOG_DEBUG("Trying to start capturing on a single thread for device [" << m_DeviceName << "]"); - - clearCoreConfiguration(); - - m_OnPacketsArriveCallback = onPacketsArrive; - m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; - - m_StopThread = false; - - m_ReentrantMode = false; - - std::mutex mutex; - std::condition_variable cond; - int startThread = 0; - - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(0, &cpuset); - m_CoreConfiguration[0].IsInUse = true; - m_CoreConfiguration[0].Channel = m_PfRingDescriptors[0]; - m_CoreConfiguration[0].RxThread = std::thread(&pcpp::PfRingDevice::captureThreadMain, this, &cond, &mutex, &startThread); - m_CoreConfiguration[0].IsAffinitySet = false; - int err = pthread_setaffinity_np(m_CoreConfiguration[0].RxThread.native_handle(), sizeof(cpu_set_t), &cpuset); - if(err != 0) - { - startThread = 1; - PCPP_LOG_ERROR("Error while binding thread to core 0: errno=" << err); - clearCoreConfiguration(); - return false; - } - startThread = 2; - cond.notify_all(); - - PCPP_LOG_DEBUG("Capturing started for device [" << m_DeviceName << "]"); - return true; -} - -void PfRingDevice::stopCapture() -{ - PCPP_LOG_DEBUG("Trying to stop capturing on device [" << m_DeviceName << "]"); - m_StopThread = true; - for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) - { - if (!m_CoreConfiguration[coreId].IsInUse) - continue; - m_CoreConfiguration[coreId].RxThread.join(); - PCPP_LOG_DEBUG("Thread on core [" << coreId << "] stopped"); - } - - PCPP_LOG_DEBUG("All capturing threads stopped"); -} - -void PfRingDevice::captureThreadMain(std::condition_variable* startCond, std::mutex* startMutex, const int* startState) -{ - while (*startState == 0) - { - std::unique_lock lock(*startMutex); - startCond->wait_for(lock, std::chrono::milliseconds(100)); - } - if (*startState == 1) - { - return; - } - - int coreId = this->getCurrentCoreId().Id; - pfring* ring = NULL; - - PCPP_LOG_DEBUG("Starting capture thread " << coreId); - - ring = this->m_CoreConfiguration[coreId].Channel; - - if (ring == NULL) - { - PCPP_LOG_ERROR("Couldn't find ring for core " << coreId << ". Exiting capture thread"); - return; - } - - while (!this->m_StopThread) - { - // if buffer is NULL PF_RING avoids copy of the data - uint8_t* buffer = NULL; - uint32_t bufferLen = 0; - - // in multi-threaded mode flag PF_RING_REENTRANT is set, and this flag doesn't work with zero copy - // so I need to allocate a buffer and set buffer to point to it - if (this->m_ReentrantMode) - { - uint8_t tempBuffer[PCPP_MAX_PACKET_SIZE]; - buffer = tempBuffer; - bufferLen = PCPP_MAX_PACKET_SIZE; - } - - struct pfring_pkthdr pktHdr; - int recvRes = pfring_recv(ring, &buffer, bufferLen, &pktHdr, 0); - if (recvRes > 0) - { - // if caplen < len it means we don't have the whole packet. Treat this case as packet drop - // TODO: add this packet to dropped packet stats -// if (pktHdr.caplen != pktHdr.len) -// { -// PCPP_LOG_ERROR("Packet dropped due to len != caplen"); -// continue; -// } - - RawPacket rawPacket(buffer, pktHdr.caplen, pktHdr.ts, false); - this->m_OnPacketsArriveCallback(&rawPacket, 1, coreId, this, this->m_OnPacketsArriveUserCookie); - } - else if (recvRes < 0) - { - // cppcheck-suppress shiftNegative - PCPP_LOG_ERROR("pfring_recv returned an error: [Err=" << recvRes << "]"); - } - } - - PCPP_LOG_DEBUG("Exiting capture thread " << coreId); -} - -void PfRingDevice::getThreadStatistics(SystemCore core, PfRingStats& stats) const -{ - pfring* ring = NULL; - uint8_t coreId = core.Id; - - ring = m_CoreConfiguration[coreId].Channel; - - if (ring != NULL) - { - pfring_stat tempStats; - if (pfring_stats(ring, &tempStats) < 0) - { - PCPP_LOG_ERROR("Can't retrieve statistics for core [" << (int)coreId << "], pfring_stats failed"); - return; - } - stats.drop = (uint64_t)tempStats.drop; - stats.recv = (uint64_t)tempStats.recv; - } - else - { - PCPP_LOG_ERROR("Core [" << (int)coreId << "] is not in use, can't retrieve statistics"); - } -} - -void PfRingDevice::getCurrentThreadStatistics(PfRingStats& stats) const -{ - getThreadStatistics(getCurrentCoreId(), stats); -} - -void PfRingDevice::getStatistics(PfRingStats& stats) const -{ - stats.drop = 0; - stats.recv = 0; - - for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) - { - if (!m_CoreConfiguration[coreId].IsInUse) - continue; - - PfRingStats tempStat = {}; - getThreadStatistics(SystemCores::IdToSystemCore[coreId], tempStat); - stats.drop += tempStat.drop; - stats.recv += tempStat.recv; - - if (!m_CoreConfiguration[coreId].IsAffinitySet) - break; - } -} - -void PfRingDevice::clearCoreConfiguration() -{ - for (int i = 0; i < MAX_NUM_OF_CORES; i++) - m_CoreConfiguration[i].clear(); -} - -int PfRingDevice::getCoresInUseCount() const -{ - int res = 0; - for (int i = 0; i < MAX_NUM_OF_CORES; i++) - if (m_CoreConfiguration[i].IsInUse) - res++; - - return res; -} - -void PfRingDevice::setPfRingDeviceAttributes() -{ - if (m_InterfaceIndex > -1) - return; - - pfring* ring = NULL; - bool closeRing = false; - if (m_NumOfOpenedRxChannels > 0) - ring = m_PfRingDescriptors[0]; - else - { - uint32_t flags = PF_RING_PROMISC | PF_RING_DNA_SYMMETRIC_RSS; - ring = pfring_open(m_DeviceName.c_str(), DEFAULT_PF_RING_SNAPLEN, flags); - closeRing = true; - } - - if (ring == NULL) - { - PCPP_LOG_ERROR("Could not open a pfring for setting device attributes: MAC address, interface index and HW clock"); - return; - } - - // set device MAC address - - uint8_t macAddress[6]; - if (pfring_get_bound_device_address(ring, macAddress) < 0) - PCPP_LOG_ERROR("Unable to read the device MAC address for interface '" << m_DeviceName << "'"); - else - m_MacAddress = MacAddress(macAddress); - - // set interface ID - if (pfring_get_bound_device_ifindex(ring, &m_InterfaceIndex) < 0) - PCPP_LOG_ERROR("Unable to read interface index of device"); - - // try to set hardware device clock - m_HwClockEnabled = setPfRingDeviceClock(ring); - - // set interface MTU - int mtu = pfring_get_mtu_size(ring); - if (mtu < 0) - // cppcheck-suppress shiftNegative - PCPP_LOG_ERROR("Could not get MTU. pfring_get_mtu_size returned an error: " << mtu); - else - m_DeviceMTU = mtu + sizeof(ether_header) + sizeof(vlan_header); - - if (Logger::getInstance().isDebugEnabled(PcapLogModulePfRingDevice)) - { - std::string hwEnabled = (m_HwClockEnabled ? "enabled" : "disabled"); - PCPP_LOG_DEBUG("Capturing from " << m_DeviceName << " [" << m_MacAddress << "][ifIndex: " << m_InterfaceIndex << "][MTU: " << m_DeviceMTU << "], HW clock " << hwEnabled); - } - - - if (closeRing) - pfring_close(ring); -} - - -bool PfRingDevice::sendData(const uint8_t* packetData, int packetDataLength, bool flushTxQueues) -{ - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device is not opened. Cannot send packets"); - return false; - } - - uint8_t flushTxAsUint = (flushTxQueues? 1 : 0); - - #define MAX_TRIES 5 - - int tries = 0; - int res = 0; - while (tries < MAX_TRIES) - { - // don't allow sending of data larger than the MTU, otherwise pfring_send will fail - if (packetDataLength > m_DeviceMTU) - packetDataLength = m_DeviceMTU; - - // if the device is opened, m_PfRingDescriptors[0] will always be set and enables - res = pfring_send(m_PfRingDescriptors[0], (char*)packetData, packetDataLength, flushTxAsUint); - - // res == -1 means it's an error coming from "sendto" which is the Linux API PF_RING is using to send packets - // errno == ENOBUFS means write buffer is full. PF_RING driver expects the userspace to handle this case - // My implementation is to sleep for 10 usec and try again - if (res == -1 && errno == ENOBUFS) - { - tries++; - PCPP_LOG_DEBUG("Try #" << tries << ": Got ENOBUFS (write buffer full) error while sending packet. Sleeping 20 usec and trying again"); - usleep(2000); - } - else - break; - } - - if (tries >= MAX_TRIES) - { - PCPP_LOG_ERROR("Tried to send data " << MAX_TRIES << " times but write buffer is full"); - return false; - } - - if (res < 0) - { - // res == -1 means it's an error coming from "sendto" which is the Linux API PF_RING is using to send packets - if (res == -1) - PCPP_LOG_ERROR("Error sending packet: Linux errno: " << strerror(errno) << " [" << errno << "]"); - else - PCPP_LOG_ERROR("Error sending packet: pfring_send returned an error: " << res << " , errno: " << strerror(errno)<< " [" << errno << "]"); - return false; - } else if (res != packetDataLength) - { - PCPP_LOG_ERROR("Couldn't send all bytes, only " << res << " bytes out of " << packetDataLength << " bytes were sent"); - return false; - } - - return true; -} - -bool PfRingDevice::sendPacket(const uint8_t* packetData, int packetDataLength) -{ - return sendData(packetData, packetDataLength, true); -} - - -bool PfRingDevice::sendPacket(const RawPacket& rawPacket) -{ - return sendData(rawPacket.getRawData(), rawPacket.getRawDataLen(), true); -} - - -bool PfRingDevice::sendPacket(const Packet& packet) -{ - return sendData(packet.getRawPacketReadOnly()->getRawData(), packet.getRawPacketReadOnly()->getRawDataLen(), true); -} - - -int PfRingDevice::sendPackets(const RawPacket* rawPacketsArr, int arrLength) -{ - int packetsSent = 0; - for (int i = 0; i < arrLength; i++) - { - if (!sendData(rawPacketsArr[i].getRawData(), rawPacketsArr[i].getRawDataLen(), false)) - break; - else - packetsSent++; - } - - // The following method isn't supported in PF_RING aware drivers, probably only in DNA and ZC - pfring_flush_tx_packets(m_PfRingDescriptors[0]); - - PCPP_LOG_DEBUG(packetsSent << " out of " << arrLength << " raw packets were sent successfully"); - - return packetsSent; -} - -int PfRingDevice::sendPackets(const Packet** packetsArr, int arrLength) -{ - int packetsSent = 0; - for (int i = 0; i < arrLength; i++) - { - if (!sendData(packetsArr[i]->getRawPacketReadOnly()->getRawData(), packetsArr[i]->getRawPacketReadOnly()->getRawDataLen(), false)) - break; - else - packetsSent++; - } - - // The following method isn't supported in PF_RING aware drivers, probably only in DNA and ZC - pfring_flush_tx_packets(m_PfRingDescriptors[0]); - - PCPP_LOG_DEBUG(packetsSent << " out of " << arrLength << " packets were sent successfully"); - - return packetsSent; -} - -int PfRingDevice::sendPackets(const RawPacketVector& rawPackets) -{ - int packetsSent = 0; - for (RawPacketVector::ConstVectorIterator iter = rawPackets.begin(); iter != rawPackets.end(); iter++) - { - if (!sendData((*iter)->getRawData(), (*iter)->getRawDataLen(), false)) - break; - else - packetsSent++; - } - - // The following method isn't supported in PF_RING aware drivers, probably only in DNA and ZC - pfring_flush_tx_packets(m_PfRingDescriptors[0]); - - PCPP_LOG_DEBUG(packetsSent << " out of " << rawPackets.size() << " raw packets were sent successfully"); - - return packetsSent; -} - -PfRingDevice::CoreConfiguration::CoreConfiguration() - : Channel(NULL), IsInUse(false), IsAffinitySet(true) -{ -} - -void PfRingDevice::CoreConfiguration::clear() -{ - Channel = NULL; - IsInUse = false; - IsAffinitySet = true; -} - -} // namespace pcpp - -#endif /* USE_PF_RING */ +#ifdef USE_PF_RING + +#define LOG_MODULE PcapLogModulePfRingDevice + +#include "PfRingDevice.h" +#include "EthLayer.h" +#include "VlanLayer.h" +#include "Logger.h" +#include +#include +#include +#include + + +#define DEFAULT_PF_RING_SNAPLEN 1600 + +namespace pcpp +{ + + +PfRingDevice::PfRingDevice(const char* deviceName) : m_MacAddress(MacAddress::Zero) +{ + m_NumOfOpenedRxChannels = 0; + m_DeviceOpened = false; + m_DeviceName = std::string(deviceName); + m_InterfaceIndex = -1; + m_StopThread = true; + m_OnPacketsArriveCallback = NULL; + m_OnPacketsArriveUserCookie = NULL; + m_ReentrantMode = false; + m_HwClockEnabled = false; + m_DeviceMTU = 0; + m_IsFilterCurrentlySet = false; + + m_PfRingDescriptors = new pfring*[MAX_NUM_RX_CHANNELS]; +} + +PfRingDevice::~PfRingDevice() +{ + close(); + delete [] m_PfRingDescriptors; +} + + +bool PfRingDevice::open() +{ + if (m_DeviceOpened) + { + PCPP_LOG_ERROR("Device already opened"); + return false; + } + + m_NumOfOpenedRxChannels = 0; + + PCPP_LOG_DEBUG("Trying to open device [" << m_DeviceName << "]"); + int res = openSingleRxChannel(m_DeviceName.c_str(), &m_PfRingDescriptors[0]); + if (res == 0) + { + PCPP_LOG_DEBUG("Succeeded opening device [" << m_DeviceName << "]"); + m_NumOfOpenedRxChannels = 1; + m_DeviceOpened = true; + return true; + } + else if (res == 1) + PCPP_LOG_ERROR("Couldn't open a ring on device [" << m_DeviceName << "]"); + else if (res == 2) + PCPP_LOG_ERROR("Unable to enable ring for device [" << m_DeviceName << "]"); + + return false; +} + + +bool PfRingDevice::openSingleRxChannel(uint8_t channelId) +{ + uint8_t channelIds[1] = { channelId }; + return openMultiRxChannels(channelIds, 1); +} + +int PfRingDevice::openSingleRxChannel(const char* deviceName, pfring** ring) +{ + if (m_DeviceOpened) + { + PCPP_LOG_ERROR("Device already opened"); + return false; + } + + uint32_t flags = PF_RING_PROMISC | PF_RING_HW_TIMESTAMP | PF_RING_DNA_SYMMETRIC_RSS; + *ring = pfring_open(deviceName, DEFAULT_PF_RING_SNAPLEN, flags); + + if (*ring == NULL) + { + return 1; + } + PCPP_LOG_DEBUG("pfring_open Succeeded for device [" << m_DeviceName << "]"); + + if (getIsHwClockEnable()) + { + setPfRingDeviceClock(*ring); + PCPP_LOG_DEBUG("H/W clock set for device [" << m_DeviceName << "]"); + } + + if (pfring_enable_rss_rehash(*ring) < 0 || pfring_enable_ring(*ring) < 0) + { + pfring_close(*ring); + return 2; + } + + PCPP_LOG_DEBUG("pfring enabled for device [" << m_DeviceName << "]"); + + return 0; +} + +bool PfRingDevice::setPfRingDeviceClock(pfring* ring) +{ + struct timespec ltime; + if (clock_gettime(CLOCK_REALTIME, <ime) != 0) + { + PCPP_LOG_ERROR("Could not set pfring devices clock, clock_gettime failed"); + return false; + } + + if (pfring_set_device_clock(ring, <ime) < 0) + { + PCPP_LOG_DEBUG("Could not set pfring devices clock, pfring_set_device_clock failed"); + return false; + } + + return true; +} + +bool PfRingDevice::openMultiRxChannels(const uint8_t* channelIds, int numOfChannelIds) +{ + if (m_DeviceOpened) + { + PCPP_LOG_ERROR("Device already opened"); + return false; + } + + // I needed to add this verification because PF_RING doesn't provide it. + // It allows opening the device on a channel that doesn't exist, but of course no packets will be captured + uint8_t totalChannels = getTotalNumOfRxChannels(); + for (int i = 0; i < numOfChannelIds; i++) + { + uint8_t channelId = channelIds[i]; + if (channelId >= totalChannels) + { + PCPP_LOG_ERROR("Trying to open the device with a RX channel that doesn't exist. Total RX channels are [" << (int)totalChannels << "], tried to open channel [" << (int)channelId << "]"); + return false; + } + } + + m_NumOfOpenedRxChannels = 0; + + for (int i = 0; i < numOfChannelIds; i++) + { + uint8_t channelId = channelIds[i]; + std::ostringstream ringNameStream; + ringNameStream << m_DeviceName << "@" << (int)channelId; + std::string ringName = ringNameStream.str(); + PCPP_LOG_DEBUG("Trying to open device [" << m_DeviceName << "] on channel [" << channelId << "]. Channel name [" << ringName << "]"); + int res = openSingleRxChannel(ringName.c_str(), &m_PfRingDescriptors[i]); + if (res == 0) + { + PCPP_LOG_DEBUG("Succeeded opening device [" << m_DeviceName << "] on channel [" << channelId << "]. Channel name [" << ringName << "]"); + m_NumOfOpenedRxChannels++; + continue; + } + else if (res == 1) + PCPP_LOG_ERROR("Couldn't open a ring on channel [" << (int)channelId << "] for device [" << m_DeviceName << "]"); + else if (res == 2) + PCPP_LOG_ERROR("Unable to enable ring on channel [" << (int)channelId << "] for device [" << m_DeviceName << "]"); + + break; + } + + if (m_NumOfOpenedRxChannels < numOfChannelIds) + { + // if an error occurred, close all rings from index=0 to index=m_NumOfOpenedRxChannels-1 + // there's no need to close m_PfRingDescriptors[m_NumOfOpenedRxChannels] because it has already been + // closed by openSingleRxChannel + for (int i = 0; i < m_NumOfOpenedRxChannels-1; i++) + { + pfring_close(m_PfRingDescriptors[i]); + } + + m_NumOfOpenedRxChannels = 0; + return false; + } + + m_DeviceOpened = true; + + return true; +} + +bool PfRingDevice::openMultiRxChannels(uint8_t numOfRxChannelsToOpen, ChannelDistribution dist) +{ + if (m_DeviceOpened) + { + PCPP_LOG_ERROR("Device already opened"); + return false; + } + + m_NumOfOpenedRxChannels = 0; + + if (numOfRxChannelsToOpen > MAX_NUM_RX_CHANNELS) + { + PCPP_LOG_ERROR("Cannot open more than [" << MAX_NUM_RX_CHANNELS << "] channels"); + return false; + } + + uint32_t flags = PF_RING_PROMISC | PF_RING_REENTRANT | PF_RING_HW_TIMESTAMP | PF_RING_DNA_SYMMETRIC_RSS; + + uint8_t numOfRxChannelsOnNIC = getTotalNumOfRxChannels(); + PCPP_LOG_DEBUG("NIC has " << (int)numOfRxChannelsOnNIC << " RX channels"); + + uint8_t numOfRingsPerRxChannel = numOfRxChannelsToOpen / numOfRxChannelsOnNIC; + uint8_t remainderRings = numOfRxChannelsToOpen % numOfRxChannelsOnNIC; + + cluster_type clusterType = (dist == RoundRobin) ? cluster_round_robin : cluster_per_flow; + + int ringsOpen = 0; + for (uint8_t channelId = 0; channelId < numOfRxChannelsOnNIC; channelId++) + { + // no more channels to open + if (numOfRingsPerRxChannel == 0 && remainderRings == 0) + break; + + std::ostringstream ringName; + ringName << m_DeviceName << "@" << (int)channelId; + + // open numOfRingsPerRxChannel rings per RX channel + for (uint8_t ringId = 0; ringId < numOfRingsPerRxChannel; ringId++) + { + m_PfRingDescriptors[ringsOpen] = pfring_open(ringName.str().c_str(), DEFAULT_PF_RING_SNAPLEN, flags); + if (m_PfRingDescriptors[ringsOpen] == NULL) + { + PCPP_LOG_ERROR("Couldn't open a ring on channel [" << (int)channelId << "]"); + break; + } + + // setting a cluster for all rings in the same channel to enable hashing between them + if (pfring_set_cluster(m_PfRingDescriptors[ringsOpen], channelId+1, clusterType) < 0) + { + PCPP_LOG_ERROR("Couldn't set ring [" << (int)ringId << "] in channel [" << (int)channelId << "] to the cluster [" << (int)(channelId+1) << "]"); + break; + } + + ringsOpen++; + } + + // open one more ring if remainder > 0 + if (remainderRings > 0) + { + m_PfRingDescriptors[ringsOpen] = pfring_open(ringName.str().c_str(), DEFAULT_PF_RING_SNAPLEN, flags); + if (m_PfRingDescriptors[ringsOpen] == NULL) + { + PCPP_LOG_ERROR("Couldn't open a ring on channel [" << (int)channelId << "]"); + break; + } + + // setting a cluster for all rings in the same channel to enable hashing between them + if (pfring_set_cluster(m_PfRingDescriptors[ringsOpen], channelId+1, clusterType) < 0) + { + PCPP_LOG_ERROR("Couldn't set ring [" << (int)(numOfRingsPerRxChannel+1) << "] in channel [" << (int)channelId << "] to the cluster [" << (int)(channelId+1) << "]"); + break; + } + + ringsOpen++; + remainderRings--; + PCPP_LOG_DEBUG("Opened " << (int)(numOfRingsPerRxChannel+1) << " rings on channel [" << (int)channelId << "]"); + } + else + PCPP_LOG_DEBUG("Opened " << (int)numOfRingsPerRxChannel << " rings on channel [" << (int)channelId << "]"); + } + + if (ringsOpen < numOfRxChannelsToOpen) + { + for (uint8_t i = 0; i < ringsOpen; i++) + pfring_close(m_PfRingDescriptors[i]); + return false; + } + + if (getIsHwClockEnable()) + { + for (int i = 0; i < ringsOpen; i++) + { + if (setPfRingDeviceClock(m_PfRingDescriptors[i])) + PCPP_LOG_DEBUG("H/W clock set for device [" << m_DeviceName << "]"); + } + } + + // enable all rings + for (int i = 0; i < ringsOpen; i++) + { + if (pfring_enable_rss_rehash(m_PfRingDescriptors[i]) < 0 || pfring_enable_ring(m_PfRingDescriptors[i]) < 0) + { + PCPP_LOG_ERROR("Unable to enable ring [" << i << "] for device [" << m_DeviceName << "]"); + // close all pfring's that were enabled until now + for (int j = 0; j 0) + { + uint8_t res = pfring_get_num_rx_channels(m_PfRingDescriptors[0]); + return res; + } + else + { + uint32_t flags = PF_RING_PROMISC | PF_RING_REENTRANT | PF_RING_HW_TIMESTAMP | PF_RING_DNA_SYMMETRIC_RSS; + pfring* ring = pfring_open(m_DeviceName.c_str(), DEFAULT_PF_RING_SNAPLEN, flags); + uint8_t res = pfring_get_num_rx_channels(ring); + pfring_close(ring); + return res; + } +} + + +SystemCore PfRingDevice::getCurrentCoreId() const +{ + return SystemCores::IdToSystemCore[sched_getcpu()]; +} + + +bool PfRingDevice::setFilter(std::string filterAsString) +{ + if (!m_DeviceOpened) + { + PCPP_LOG_ERROR("Device not opened"); + return false; + } + + for (int i = 0; i < m_NumOfOpenedRxChannels; i++) + { + int res = pfring_set_bpf_filter(m_PfRingDescriptors[i], (char*)filterAsString.c_str()); + if(res < 0) + { + if (res == PF_RING_ERROR_NOT_SUPPORTED) + PCPP_LOG_ERROR("BPF filtering isn't supported on current PF_RING version. Please re-compile PF_RING with the --enable-bpf flag"); + else + PCPP_LOG_ERROR("Couldn't set filter '" << filterAsString << "'"); + return false; + } + } + + m_IsFilterCurrentlySet = true; + + PCPP_LOG_DEBUG("Successfully set filter '" << filterAsString << "'"); + return true; +} + + +bool PfRingDevice::clearFilter() +{ + if (!m_IsFilterCurrentlySet) + return true; + + for (int i = 0; i < m_NumOfOpenedRxChannels; i++) + { + int res = pfring_remove_bpf_filter(m_PfRingDescriptors[i]); + if(res < 0) + { + PCPP_LOG_ERROR("Couldn't remove filter"); + return false; + } + } + + m_IsFilterCurrentlySet = false; + + PCPP_LOG_DEBUG("Successfully removed filter from all open RX channels"); + return true; +} + + +bool PfRingDevice::isFilterCurrentlySet() const +{ + return m_IsFilterCurrentlySet; +} + + +void PfRingDevice::close() +{ + for (int i = 0; i < m_NumOfOpenedRxChannels; i++) + pfring_close(m_PfRingDescriptors[i]); + m_DeviceOpened = false; + clearCoreConfiguration(); + m_NumOfOpenedRxChannels = 0; + m_IsFilterCurrentlySet = false; + PCPP_LOG_DEBUG("Device [" << m_DeviceName << "] closed"); +} + +bool PfRingDevice::initCoreConfigurationByCoreMask(CoreMask coreMask) +{ + int i = 0; + int numOfCores = getNumOfCores(); + clearCoreConfiguration(); + while ((coreMask != 0) && (i < numOfCores)) + { + if (coreMask & 1) + { + m_CoreConfiguration[i].IsInUse = true; + } + + coreMask = coreMask >> 1; + i++; + } + + if (coreMask != 0) // this mean coreMask contains a core that doesn't exist + { + PCPP_LOG_ERROR("Trying to use a core [" << i << "] that doesn't exist while machine has " << numOfCores << " cores"); + clearCoreConfiguration(); + return false; + } + + return true; +} + +bool PfRingDevice::startCaptureMultiThread(OnPfRingPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie, CoreMask coreMask) +{ + if (!m_StopThread) + { + PCPP_LOG_ERROR("Device already capturing. Cannot start 2 capture sessions at the same time"); + return false; + } + + if (!initCoreConfigurationByCoreMask(coreMask)) + return false; + + if (m_NumOfOpenedRxChannels != getCoresInUseCount()) + { + PCPP_LOG_ERROR("Cannot use a different number of channels and cores. Opened " << m_NumOfOpenedRxChannels << " channels but set " << getCoresInUseCount() << " cores in core mask"); + clearCoreConfiguration(); + return false; + } + + std::mutex mutex; + std::condition_variable cond; + int startThread = 0; + + m_StopThread = false; + int rxChannel = 0; + for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) + { + if (!m_CoreConfiguration[coreId].IsInUse) + continue; + + m_ReentrantMode = true; + + m_OnPacketsArriveCallback = onPacketsArrive; + m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; + + // create a new thread + m_CoreConfiguration[coreId].Channel = m_PfRingDescriptors[rxChannel++]; + m_CoreConfiguration[coreId].RxThread = std::thread(&pcpp::PfRingDevice::captureThreadMain, this, &cond, &mutex, &startThread); + + // set affinity to cores + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(coreId, &cpuset); + int err = pthread_setaffinity_np(m_CoreConfiguration[coreId].RxThread.native_handle(), sizeof(cpu_set_t), &cpuset); + if(err != 0) + { + PCPP_LOG_ERROR("Error while binding thread to core " << coreId << ": errno=" << err); + startThread = 1; + clearCoreConfiguration(); + return false; + } + } + + startThread = 2; + cond.notify_all(); + + return true; +} + +bool PfRingDevice::startCaptureSingleThread(OnPfRingPacketsArriveCallback onPacketsArrive, void* onPacketsArriveUserCookie) +{ + if (!m_StopThread) + { + PCPP_LOG_ERROR("Device already capturing. Cannot start 2 capture sessions at the same time"); + return false; + } + + if (m_NumOfOpenedRxChannels != 1) + { + PCPP_LOG_ERROR("Cannot start capturing on a single thread when more than 1 RX channel is opened"); + return false; + } + + PCPP_LOG_DEBUG("Trying to start capturing on a single thread for device [" << m_DeviceName << "]"); + + clearCoreConfiguration(); + + m_OnPacketsArriveCallback = onPacketsArrive; + m_OnPacketsArriveUserCookie = onPacketsArriveUserCookie; + + m_StopThread = false; + + m_ReentrantMode = false; + + std::mutex mutex; + std::condition_variable cond; + int startThread = 0; + + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(0, &cpuset); + m_CoreConfiguration[0].IsInUse = true; + m_CoreConfiguration[0].Channel = m_PfRingDescriptors[0]; + m_CoreConfiguration[0].RxThread = std::thread(&pcpp::PfRingDevice::captureThreadMain, this, &cond, &mutex, &startThread); + m_CoreConfiguration[0].IsAffinitySet = false; + int err = pthread_setaffinity_np(m_CoreConfiguration[0].RxThread.native_handle(), sizeof(cpu_set_t), &cpuset); + if(err != 0) + { + startThread = 1; + PCPP_LOG_ERROR("Error while binding thread to core 0: errno=" << err); + clearCoreConfiguration(); + return false; + } + startThread = 2; + cond.notify_all(); + + PCPP_LOG_DEBUG("Capturing started for device [" << m_DeviceName << "]"); + return true; +} + +void PfRingDevice::stopCapture() +{ + PCPP_LOG_DEBUG("Trying to stop capturing on device [" << m_DeviceName << "]"); + m_StopThread = true; + for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) + { + if (!m_CoreConfiguration[coreId].IsInUse) + continue; + m_CoreConfiguration[coreId].RxThread.join(); + PCPP_LOG_DEBUG("Thread on core [" << coreId << "] stopped"); + } + + PCPP_LOG_DEBUG("All capturing threads stopped"); +} + +void PfRingDevice::captureThreadMain(std::condition_variable* startCond, std::mutex* startMutex, const int* startState) +{ + while (*startState == 0) + { + std::unique_lock lock(*startMutex); + startCond->wait_for(lock, std::chrono::milliseconds(100)); + } + if (*startState == 1) + { + return; + } + + int coreId = this->getCurrentCoreId().Id; + pfring* ring = NULL; + + PCPP_LOG_DEBUG("Starting capture thread " << coreId); + + ring = this->m_CoreConfiguration[coreId].Channel; + + if (ring == NULL) + { + PCPP_LOG_ERROR("Couldn't find ring for core " << coreId << ". Exiting capture thread"); + return; + } + + while (!this->m_StopThread) + { + // if buffer is NULL PF_RING avoids copy of the data + uint8_t* buffer = NULL; + uint32_t bufferLen = 0; + + // in multi-threaded mode flag PF_RING_REENTRANT is set, and this flag doesn't work with zero copy + // so I need to allocate a buffer and set buffer to point to it + if (this->m_ReentrantMode) + { + uint8_t tempBuffer[PCPP_MAX_PACKET_SIZE]; + buffer = tempBuffer; + bufferLen = PCPP_MAX_PACKET_SIZE; + } + + struct pfring_pkthdr pktHdr; + int recvRes = pfring_recv(ring, &buffer, bufferLen, &pktHdr, 0); + if (recvRes > 0) + { + // if caplen < len it means we don't have the whole packet. Treat this case as packet drop + // TODO: add this packet to dropped packet stats +// if (pktHdr.caplen != pktHdr.len) +// { +// PCPP_LOG_ERROR("Packet dropped due to len != caplen"); +// continue; +// } + + RawPacket rawPacket(buffer, pktHdr.caplen, pktHdr.ts, false); + this->m_OnPacketsArriveCallback(&rawPacket, 1, coreId, this, this->m_OnPacketsArriveUserCookie); + } + else if (recvRes < 0) + { + // cppcheck-suppress shiftNegative + PCPP_LOG_ERROR("pfring_recv returned an error: [Err=" << recvRes << "]"); + } + } + + PCPP_LOG_DEBUG("Exiting capture thread " << coreId); +} + +void PfRingDevice::getThreadStatistics(SystemCore core, PfRingStats& stats) const +{ + pfring* ring = NULL; + uint8_t coreId = core.Id; + + ring = m_CoreConfiguration[coreId].Channel; + + if (ring != NULL) + { + pfring_stat tempStats; + if (pfring_stats(ring, &tempStats) < 0) + { + PCPP_LOG_ERROR("Can't retrieve statistics for core [" << (int)coreId << "], pfring_stats failed"); + return; + } + stats.drop = (uint64_t)tempStats.drop; + stats.recv = (uint64_t)tempStats.recv; + } + else + { + PCPP_LOG_ERROR("Core [" << (int)coreId << "] is not in use, can't retrieve statistics"); + } +} + +void PfRingDevice::getCurrentThreadStatistics(PfRingStats& stats) const +{ + getThreadStatistics(getCurrentCoreId(), stats); +} + +void PfRingDevice::getStatistics(PfRingStats& stats) const +{ + stats.drop = 0; + stats.recv = 0; + + for (int coreId = 0; coreId < MAX_NUM_OF_CORES; coreId++) + { + if (!m_CoreConfiguration[coreId].IsInUse) + continue; + + PfRingStats tempStat = {}; + getThreadStatistics(SystemCores::IdToSystemCore[coreId], tempStat); + stats.drop += tempStat.drop; + stats.recv += tempStat.recv; + + if (!m_CoreConfiguration[coreId].IsAffinitySet) + break; + } +} + +void PfRingDevice::clearCoreConfiguration() +{ + for (int i = 0; i < MAX_NUM_OF_CORES; i++) + m_CoreConfiguration[i].clear(); +} + +int PfRingDevice::getCoresInUseCount() const +{ + int res = 0; + for (int i = 0; i < MAX_NUM_OF_CORES; i++) + if (m_CoreConfiguration[i].IsInUse) + res++; + + return res; +} + +void PfRingDevice::setPfRingDeviceAttributes() +{ + if (m_InterfaceIndex > -1) + return; + + pfring* ring = NULL; + bool closeRing = false; + if (m_NumOfOpenedRxChannels > 0) + ring = m_PfRingDescriptors[0]; + else + { + uint32_t flags = PF_RING_PROMISC | PF_RING_DNA_SYMMETRIC_RSS; + ring = pfring_open(m_DeviceName.c_str(), DEFAULT_PF_RING_SNAPLEN, flags); + closeRing = true; + } + + if (ring == NULL) + { + PCPP_LOG_ERROR("Could not open a pfring for setting device attributes: MAC address, interface index and HW clock"); + return; + } + + // set device MAC address + + uint8_t macAddress[6]; + if (pfring_get_bound_device_address(ring, macAddress) < 0) + PCPP_LOG_ERROR("Unable to read the device MAC address for interface '" << m_DeviceName << "'"); + else + m_MacAddress = MacAddress(macAddress); + + // set interface ID + if (pfring_get_bound_device_ifindex(ring, &m_InterfaceIndex) < 0) + PCPP_LOG_ERROR("Unable to read interface index of device"); + + // try to set hardware device clock + m_HwClockEnabled = setPfRingDeviceClock(ring); + + // set interface MTU + int mtu = pfring_get_mtu_size(ring); + if (mtu < 0) + // cppcheck-suppress shiftNegative + PCPP_LOG_ERROR("Could not get MTU. pfring_get_mtu_size returned an error: " << mtu); + else + m_DeviceMTU = mtu + sizeof(ether_header) + sizeof(vlan_header); + + if (Logger::getInstance().isDebugEnabled(PcapLogModulePfRingDevice)) + { + std::string hwEnabled = (m_HwClockEnabled ? "enabled" : "disabled"); + PCPP_LOG_DEBUG("Capturing from " << m_DeviceName << " [" << m_MacAddress << "][ifIndex: " << m_InterfaceIndex << "][MTU: " << m_DeviceMTU << "], HW clock " << hwEnabled); + } + + + if (closeRing) + pfring_close(ring); +} + + +bool PfRingDevice::sendData(const uint8_t* packetData, int packetDataLength, bool flushTxQueues) +{ + if (!m_DeviceOpened) + { + PCPP_LOG_ERROR("Device is not opened. Cannot send packets"); + return false; + } + + uint8_t flushTxAsUint = (flushTxQueues? 1 : 0); + + #define MAX_TRIES 5 + + int tries = 0; + int res = 0; + while (tries < MAX_TRIES) + { + // don't allow sending of data larger than the MTU, otherwise pfring_send will fail + if (packetDataLength > m_DeviceMTU) + packetDataLength = m_DeviceMTU; + + // if the device is opened, m_PfRingDescriptors[0] will always be set and enables + res = pfring_send(m_PfRingDescriptors[0], (char*)packetData, packetDataLength, flushTxAsUint); + + // res == -1 means it's an error coming from "sendto" which is the Linux API PF_RING is using to send packets + // errno == ENOBUFS means write buffer is full. PF_RING driver expects the userspace to handle this case + // My implementation is to sleep for 10 usec and try again + if (res == -1 && errno == ENOBUFS) + { + tries++; + PCPP_LOG_DEBUG("Try #" << tries << ": Got ENOBUFS (write buffer full) error while sending packet. Sleeping 20 usec and trying again"); + usleep(2000); + } + else + break; + } + + if (tries >= MAX_TRIES) + { + PCPP_LOG_ERROR("Tried to send data " << MAX_TRIES << " times but write buffer is full"); + return false; + } + + if (res < 0) + { + // res == -1 means it's an error coming from "sendto" which is the Linux API PF_RING is using to send packets + if (res == -1) + PCPP_LOG_ERROR("Error sending packet: Linux errno: " << strerror(errno) << " [" << errno << "]"); + else + PCPP_LOG_ERROR("Error sending packet: pfring_send returned an error: " << res << " , errno: " << strerror(errno)<< " [" << errno << "]"); + return false; + } else if (res != packetDataLength) + { + PCPP_LOG_ERROR("Couldn't send all bytes, only " << res << " bytes out of " << packetDataLength << " bytes were sent"); + return false; + } + + return true; +} + +bool PfRingDevice::sendPacket(const uint8_t* packetData, int packetDataLength) +{ + return sendData(packetData, packetDataLength, true); +} + + +bool PfRingDevice::sendPacket(const RawPacket& rawPacket) +{ + return sendData(rawPacket.getRawData(), rawPacket.getRawDataLen(), true); +} + + +bool PfRingDevice::sendPacket(const Packet& packet) +{ + return sendData(packet.getRawPacketReadOnly()->getRawData(), packet.getRawPacketReadOnly()->getRawDataLen(), true); +} + + +int PfRingDevice::sendPackets(const RawPacket* rawPacketsArr, int arrLength) +{ + int packetsSent = 0; + for (int i = 0; i < arrLength; i++) + { + if (!sendData(rawPacketsArr[i].getRawData(), rawPacketsArr[i].getRawDataLen(), false)) + break; + else + packetsSent++; + } + + // The following method isn't supported in PF_RING aware drivers, probably only in DNA and ZC + pfring_flush_tx_packets(m_PfRingDescriptors[0]); + + PCPP_LOG_DEBUG(packetsSent << " out of " << arrLength << " raw packets were sent successfully"); + + return packetsSent; +} + +int PfRingDevice::sendPackets(const Packet** packetsArr, int arrLength) +{ + int packetsSent = 0; + for (int i = 0; i < arrLength; i++) + { + if (!sendData(packetsArr[i]->getRawPacketReadOnly()->getRawData(), packetsArr[i]->getRawPacketReadOnly()->getRawDataLen(), false)) + break; + else + packetsSent++; + } + + // The following method isn't supported in PF_RING aware drivers, probably only in DNA and ZC + pfring_flush_tx_packets(m_PfRingDescriptors[0]); + + PCPP_LOG_DEBUG(packetsSent << " out of " << arrLength << " packets were sent successfully"); + + return packetsSent; +} + +int PfRingDevice::sendPackets(const RawPacketVector& rawPackets) +{ + int packetsSent = 0; + for (RawPacketVector::ConstVectorIterator iter = rawPackets.begin(); iter != rawPackets.end(); iter++) + { + if (!sendData((*iter)->getRawData(), (*iter)->getRawDataLen(), false)) + break; + else + packetsSent++; + } + + // The following method isn't supported in PF_RING aware drivers, probably only in DNA and ZC + pfring_flush_tx_packets(m_PfRingDescriptors[0]); + + PCPP_LOG_DEBUG(packetsSent << " out of " << rawPackets.size() << " raw packets were sent successfully"); + + return packetsSent; +} + +PfRingDevice::CoreConfiguration::CoreConfiguration() + : Channel(NULL), IsInUse(false), IsAffinitySet(true) +{ +} + +void PfRingDevice::CoreConfiguration::clear() +{ + Channel = NULL; + IsInUse = false; + IsAffinitySet = true; +} + +} // namespace pcpp + +#endif /* USE_PF_RING */ diff --git a/Pcap++/src/PfRingDeviceList.cpp b/Pcap++/src/PfRingDeviceList.cpp index d0ed151a17..c9d5e1a1bd 100644 --- a/Pcap++/src/PfRingDeviceList.cpp +++ b/Pcap++/src/PfRingDeviceList.cpp @@ -1,101 +1,101 @@ -#ifdef USE_PF_RING - -#define LOG_MODULE PcapLogModulePfRingDevice - -#include "PfRingDeviceList.h" -#include "Logger.h" -#include "pcap.h" -#include "pfring.h" - -namespace pcpp -{ - -PfRingDeviceList::PfRingDeviceList() -{ - m_PfRingVersion = ""; - - FILE *fd = popen("lsmod | grep pf_ring", "r"); - char buf[16]; - if (!fread(buf, 1, sizeof (buf), fd)) // if there is some result the module must be loaded - { - PCPP_LOG_ERROR("PF_RING kernel module isn't loaded. Please run: 'sudo insmod /kernel/pf_ring.ko'"); - return; - } - - PCPP_LOG_DEBUG("PF_RING kernel module is loaded"); - - pcap_if_t* interfaceList; - char errbuf[PCAP_ERRBUF_SIZE]; - PCPP_LOG_DEBUG("PfRingDeviceList init: searching all interfaces on machine"); - int err = pcap_findalldevs(&interfaceList, errbuf); - if (err < 0) - { - PCPP_LOG_ERROR("Error searching for PF_RING devices: " << errbuf); - } - - pcap_if_t* currInterface = interfaceList; - while (currInterface != NULL) - { - uint32_t flags = PF_RING_PROMISC | PF_RING_DNA_SYMMETRIC_RSS; - pfring* ring = pfring_open(currInterface->name, 128, flags); - if (ring != NULL) - { - if (m_PfRingVersion == "") - calcPfRingVersion(ring); - pfring_close(ring); - PfRingDevice* newDev = new PfRingDevice(currInterface->name); - m_PfRingDeviceList.push_back(newDev); - PCPP_LOG_DEBUG("Found interface: " << currInterface->name); - } - - currInterface = currInterface->next; - } - - PCPP_LOG_DEBUG("PfRingDeviceList init end"); - pcap_freealldevs(interfaceList); -} - -PfRingDeviceList::~PfRingDeviceList() -{ - for(std::vector::iterator devIter = m_PfRingDeviceList.begin(); devIter != m_PfRingDeviceList.end(); devIter++) - { - delete (*devIter); - } -} - -PfRingDevice* PfRingDeviceList::getPfRingDeviceByName(const std::string &devName) const -{ - PCPP_LOG_DEBUG("Searching all live devices..."); - for(std::vector::const_iterator devIter = m_PfRingDeviceList.begin(); devIter != m_PfRingDeviceList.end(); devIter++) - { - if ((*devIter)->getDeviceName() == devName) - return (*devIter); - } - - PCPP_LOG_DEBUG("Found no PF_RING devices with name '" << devName << "'"); - return NULL; -} - -void PfRingDeviceList::calcPfRingVersion(void* ring) -{ - pfring* ringPtr = (pfring*)ring; - uint32_t version; - if (pfring_version(ringPtr, &version) < 0) - { - PCPP_LOG_ERROR("Couldn't retrieve PF_RING version, pfring_version returned an error"); - return; - } - - char versionAsString[25]; - sprintf(versionAsString, "PF_RING v.%u.%u.%u\n", - (version & 0xFFFF0000) >> 16, - (version & 0x0000FF00) >> 8, - version & 0x000000FF); - - PCPP_LOG_DEBUG("PF_RING version is: " << versionAsString); - m_PfRingVersion = std::string(versionAsString); -} - -} // namespace pcpp - -#endif /* USE_PF_RING */ +#ifdef USE_PF_RING + +#define LOG_MODULE PcapLogModulePfRingDevice + +#include "PfRingDeviceList.h" +#include "Logger.h" +#include "pcap.h" +#include "pfring.h" + +namespace pcpp +{ + +PfRingDeviceList::PfRingDeviceList() +{ + m_PfRingVersion = ""; + + FILE *fd = popen("lsmod | grep pf_ring", "r"); + char buf[16]; + if (!fread(buf, 1, sizeof (buf), fd)) // if there is some result the module must be loaded + { + PCPP_LOG_ERROR("PF_RING kernel module isn't loaded. Please run: 'sudo insmod /kernel/pf_ring.ko'"); + return; + } + + PCPP_LOG_DEBUG("PF_RING kernel module is loaded"); + + pcap_if_t* interfaceList; + char errbuf[PCAP_ERRBUF_SIZE]; + PCPP_LOG_DEBUG("PfRingDeviceList init: searching all interfaces on machine"); + int err = pcap_findalldevs(&interfaceList, errbuf); + if (err < 0) + { + PCPP_LOG_ERROR("Error searching for PF_RING devices: " << errbuf); + } + + pcap_if_t* currInterface = interfaceList; + while (currInterface != NULL) + { + uint32_t flags = PF_RING_PROMISC | PF_RING_DNA_SYMMETRIC_RSS; + pfring* ring = pfring_open(currInterface->name, 128, flags); + if (ring != NULL) + { + if (m_PfRingVersion == "") + calcPfRingVersion(ring); + pfring_close(ring); + PfRingDevice* newDev = new PfRingDevice(currInterface->name); + m_PfRingDeviceList.push_back(newDev); + PCPP_LOG_DEBUG("Found interface: " << currInterface->name); + } + + currInterface = currInterface->next; + } + + PCPP_LOG_DEBUG("PfRingDeviceList init end"); + pcap_freealldevs(interfaceList); +} + +PfRingDeviceList::~PfRingDeviceList() +{ + for(std::vector::iterator devIter = m_PfRingDeviceList.begin(); devIter != m_PfRingDeviceList.end(); devIter++) + { + delete (*devIter); + } +} + +PfRingDevice* PfRingDeviceList::getPfRingDeviceByName(const std::string &devName) const +{ + PCPP_LOG_DEBUG("Searching all live devices..."); + for(std::vector::const_iterator devIter = m_PfRingDeviceList.begin(); devIter != m_PfRingDeviceList.end(); devIter++) + { + if ((*devIter)->getDeviceName() == devName) + return (*devIter); + } + + PCPP_LOG_DEBUG("Found no PF_RING devices with name '" << devName << "'"); + return NULL; +} + +void PfRingDeviceList::calcPfRingVersion(void* ring) +{ + pfring* ringPtr = (pfring*)ring; + uint32_t version; + if (pfring_version(ringPtr, &version) < 0) + { + PCPP_LOG_ERROR("Couldn't retrieve PF_RING version, pfring_version returned an error"); + return; + } + + char versionAsString[25]; + sprintf(versionAsString, "PF_RING v.%u.%u.%u\n", + (version & 0xFFFF0000) >> 16, + (version & 0x0000FF00) >> 8, + version & 0x000000FF); + + PCPP_LOG_DEBUG("PF_RING version is: " << versionAsString); + m_PfRingVersion = std::string(versionAsString); +} + +} // namespace pcpp + +#endif /* USE_PF_RING */ diff --git a/Pcap++/src/RawSocketDevice.cpp b/Pcap++/src/RawSocketDevice.cpp index b0d9614ad2..8db2fed05f 100644 --- a/Pcap++/src/RawSocketDevice.cpp +++ b/Pcap++/src/RawSocketDevice.cpp @@ -1,572 +1,572 @@ -#include "RawSocketDevice.h" -#include "EndianPortable.h" -#ifdef __linux__ -#include -#include -#include -#include -#include -#include -#include -#endif -#include -#include "Logger.h" -#include "IpUtils.h" -#include "SystemUtils.h" -#include "Packet.h" -#include "EthLayer.h" - -namespace pcpp -{ - -#define RAW_SOCKET_BUFFER_LEN 65536 - -#if defined(_WIN32) - -#ifndef SIO_RCVALL -/* SIO_RCVALL defined on w2k and later. Not defined in Mingw32 */ -/* 0x98000001 = _WSAIOW(IOC_VENDOR,1) */ -# define SIO_RCVALL 0x98000001 -#endif // SIO_RCVALL - -class WinSockInitializer -{ -private: - static bool m_IsInitialized; - -public: - - static void initialize() - { - if (m_IsInitialized) - return; - - // Load Winsock - WSADATA wsaData; - int res = WSAStartup(MAKEWORD(2,2), &wsaData); - if (res != 0) - { - PCPP_LOG_ERROR("WSAStartup failed with error code: " << res); - m_IsInitialized = false; - } - - m_IsInitialized = true; - } -}; - -bool WinSockInitializer::m_IsInitialized = false; - -#endif // defined(_WIN32) - -struct SocketContainer -{ -#if defined(_WIN32) - SOCKET fd; -#elif defined(__linux__) - int fd; - int interfaceIndex; - std::string interfaceName; -#endif -}; - -RawSocketDevice::RawSocketDevice(const IPAddress& interfaceIP) : IDevice(), m_Socket(nullptr) -{ -#if defined(_WIN32) - - WinSockInitializer::initialize(); - m_InterfaceIP = interfaceIP; - m_SockFamily = (m_InterfaceIP.getType() == IPAddress::IPv4AddressType ? IPv4 : IPv6); - -#elif defined(__linux__) - - m_InterfaceIP = interfaceIP; - m_SockFamily = Ethernet; - -#else - - m_SockFamily = Ethernet; - -#endif -} - - -RawSocketDevice::~RawSocketDevice() -{ - close(); -} - -RawSocketDevice::RecvPacketResult RawSocketDevice::receivePacket(RawPacket& rawPacket, bool blocking, int timeout) -{ -#if defined(_WIN32) - - if (!isOpened()) - { - PCPP_LOG_ERROR("Device is not open"); - return RecvError; - } - - SOCKET fd = ((SocketContainer*)m_Socket)->fd; - char* buffer = new char[RAW_SOCKET_BUFFER_LEN]; - memset(buffer, 0, RAW_SOCKET_BUFFER_LEN); - - // value of 0 timeout means disabling timeout - if (timeout < 0) - timeout = 0; - - u_long blockingMode = (blocking? 0 : 1); - ioctlsocket(fd, FIONBIO, &blockingMode); - - DWORD timeoutVal = timeout * 1000; - setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutVal, sizeof(timeoutVal)); - - //recvfrom(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0, (struct sockaddr*)&sockAddr,(socklen_t*)&sockAddrLen); - int bufferLen = recv(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0); - if (bufferLen < 0) - { - delete [] buffer; - int errorCode = 0; - RecvPacketResult error = getError(errorCode); - - if (error == RecvError) - PCPP_LOG_ERROR("Error reading from recvfrom. Error code is " << errorCode); - - return error; - } - - if (bufferLen > 0) - { - timeval time; - gettimeofday(&time, NULL); - rawPacket.setRawData((const uint8_t*)buffer, bufferLen, time, LINKTYPE_DLT_RAW1); - return RecvSuccess; - } - - PCPP_LOG_ERROR("Buffer length is zero"); - delete [] buffer; - return RecvError; - -#elif defined(__linux__) - - if (!isOpened()) - { - PCPP_LOG_ERROR("Device is not open"); - return RecvError; - } - - int fd = ((SocketContainer*)m_Socket)->fd; - char* buffer = new char[RAW_SOCKET_BUFFER_LEN]; - memset(buffer, 0, RAW_SOCKET_BUFFER_LEN); - - // value of 0 timeout means disabling timeout - if (timeout < 0) - timeout = 0; - - // set blocking or non-blocking flag - int flags = fcntl(fd, F_GETFL, 0); - if (flags == -1) - { - delete [] buffer; - PCPP_LOG_ERROR("Cannot get socket flags"); - return RecvError; - } - flags = (blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)); - if (fcntl(fd, F_SETFL, flags) != 0) - { - delete [] buffer; - PCPP_LOG_ERROR("Cannot set socket non-blocking flag"); - return RecvError; - } - - // set timeout on socket - struct timeval timeoutVal; - timeoutVal.tv_sec = timeout; - timeoutVal.tv_usec = 0; - setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutVal, sizeof(timeoutVal)); - - int bufferLen = recv(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0); - if (bufferLen < 0) - { - delete [] buffer; - int errorCode = errno; - RecvPacketResult error = getError(errorCode); - - if (error == RecvError) - PCPP_LOG_ERROR("Error reading from recvfrom. Error code is " << errorCode); - - return error; - } - - if (bufferLen > 0) - { - timeval time; - gettimeofday(&time, NULL); - rawPacket.setRawData((const uint8_t*)buffer, bufferLen, time, LINKTYPE_ETHERNET); - return RecvSuccess; - } - - PCPP_LOG_ERROR("Buffer length is zero"); - delete [] buffer; - return RecvError; - -#else - - PCPP_LOG_ERROR("Raw socket are not supported on this platform"); - return RecvError; - -#endif -} - -int RawSocketDevice::receivePackets(RawPacketVector& packetVec, int timeout, int& failedRecv) -{ - if (!isOpened()) - { - PCPP_LOG_ERROR("Device is not open"); - return 0; - } - - long curSec, curNsec; - clockGetTime(curSec, curNsec); - - int packetCount = 0; - failedRecv = 0; - - long timeoutSec = curSec + timeout; - - while (curSec < timeoutSec) - { - RawPacket* rawPacket = new RawPacket(); - if (receivePacket(*rawPacket, true, timeoutSec-curSec) == RecvSuccess) - { - packetVec.pushBack(rawPacket); - packetCount++; - } - else - { - failedRecv++; - delete rawPacket; - } - - clockGetTime(curSec, curNsec); - } - - return packetCount; -} - -bool RawSocketDevice::sendPacket(const RawPacket* rawPacket) -{ -#if defined(_WIN32) - - PCPP_LOG_ERROR("Sending packets with raw socket are not supported on Windows"); - return 0; - -#elif defined(__linux__) - - if (!isOpened()) - { - PCPP_LOG_ERROR("Device is not open"); - return false; - } - - Packet packet((RawPacket*)rawPacket, OsiModelDataLinkLayer); - if (!packet.isPacketOfType(pcpp::Ethernet)) - { - PCPP_LOG_ERROR("Can't send non-Ethernet packets"); - return false; - } - - int fd = ((SocketContainer*)m_Socket)->fd; - - sockaddr_ll addr; - memset(&addr, 0, sizeof(struct sockaddr_ll)); - addr.sll_family = htobe16(PF_PACKET); - addr.sll_protocol = htobe16(ETH_P_ALL); - addr.sll_halen = 6; - addr.sll_ifindex = ((SocketContainer*)m_Socket)->interfaceIndex; - - EthLayer* ethLayer = packet.getLayerOfType(); - MacAddress dstMac = ethLayer->getDestMac(); - dstMac.copyTo((uint8_t*)&(addr.sll_addr)); - - if (::sendto(fd, ((RawPacket*)rawPacket)->getRawData(), ((RawPacket*)rawPacket)->getRawDataLen(), 0, (struct sockaddr*)&addr, sizeof(addr)) == -1) - { - PCPP_LOG_ERROR("Failed to send packet. Error was: '" << strerror(errno) << "'"); - return false; - } - - return true; - -#else - - PCPP_LOG_ERROR("Raw socket are not supported on this platform"); - return 0; - -#endif -} - -int RawSocketDevice::sendPackets(const RawPacketVector& packetVec) -{ -#if defined(_WIN32) - - PCPP_LOG_ERROR("Sending packets with raw socket are not supported on Windows"); - return false; - -#elif defined(__linux__) - - if (!isOpened()) - { - PCPP_LOG_ERROR("Device is not open"); - return 0; - } - - int fd = ((SocketContainer*)m_Socket)->fd; - - sockaddr_ll addr; - memset(&addr, 0, sizeof(struct sockaddr_ll)); - addr.sll_family = htobe16(PF_PACKET); - addr.sll_protocol = htobe16(ETH_P_ALL); - addr.sll_halen = 6; - addr.sll_ifindex = ((SocketContainer*)m_Socket)->interfaceIndex; - - int sendCount = 0; - - for (RawPacketVector::ConstVectorIterator iter = packetVec.begin(); iter != packetVec.end(); iter++) - { - Packet packet(*iter, OsiModelDataLinkLayer); - if (!packet.isPacketOfType(pcpp::Ethernet)) - { - PCPP_LOG_DEBUG("Can't send non-Ethernet packets"); - continue; - } - - EthLayer* ethLayer = packet.getLayerOfType(); - MacAddress dstMac = ethLayer->getDestMac(); - dstMac.copyTo((uint8_t*)&(addr.sll_addr)); - - if (::sendto(fd, (*iter)->getRawData(), (*iter)->getRawDataLen(), 0, (struct sockaddr*)&addr, sizeof(addr)) == -1) - { - PCPP_LOG_DEBUG("Failed to send packet. Error was: '" << strerror(errno) << "'"); - continue; - } - - sendCount++; - } - - return sendCount; - -#else - - PCPP_LOG_ERROR("Raw socket are not supported on this platform"); - return false; - -#endif -} - - -bool RawSocketDevice::open() -{ -#if defined(_WIN32) - - if (!m_InterfaceIP.isValid()) - { - PCPP_LOG_ERROR("IP address is not valid"); - return false; - } - - int family = (m_SockFamily == IPv4 ? AF_INET : AF_INET6); - SOCKET fd = socket(family, SOCK_RAW, IPPROTO_IP); - if ((int)fd == SOCKET_ERROR) - { - int error = WSAGetLastError(); - std::string additionalMessage = ""; - if (error == WSAEACCES) - additionalMessage = ", you may not be running with administrative privileges which is required for opening raw sockets on Windows"; - PCPP_LOG_ERROR("Failed to create raw socket. Error code was " << error << " " << additionalMessage); - return false; - } - - void* localAddr = NULL; - struct sockaddr_in localAddrIPv4; - struct sockaddr_in6 localAddrIPv6; - size_t localAddrSize = 0; - - if (m_SockFamily == IPv4) - { - localAddrIPv4.sin_family = family; - int res = inet_pton(family, m_InterfaceIP.toString().c_str(), &localAddrIPv4.sin_addr.s_addr); - if (res <= 0) - { - PCPP_LOG_ERROR("inet_pton failed, probably IP address provided is in bad format"); - closesocket(fd); - return false; - } - localAddrIPv4.sin_port = 0; // Any local port will do - localAddr = &localAddrIPv4; - localAddrSize = sizeof(localAddrIPv4); - } - else - { - localAddrIPv6.sin6_family = family; - int res = inet_pton(AF_INET6, m_InterfaceIP.toString().c_str(), &localAddrIPv6.sin6_addr.s6_addr); - if (res <= 0) - { - PCPP_LOG_ERROR("inet_pton failed, probably IP address provided is in bad format"); - closesocket(fd); - return false; - } - localAddrIPv6.sin6_port = 0; // Any local port will do - localAddrIPv6.sin6_scope_id = 0; - localAddr = &localAddrIPv6; - localAddrSize = sizeof(localAddrIPv6); - } - - if (bind(fd, (struct sockaddr *)localAddr, localAddrSize) == SOCKET_ERROR) - { - PCPP_LOG_ERROR("Failed to bind to interface. Error code was '" << WSAGetLastError() << "'"); - closesocket(fd); - return false; - } - - int n = 1; - DWORD dwBytesRet; - if (WSAIoctl(fd, SIO_RCVALL, &n, sizeof(n), NULL, 0, &dwBytesRet, NULL, NULL) == SOCKET_ERROR) - { - PCPP_LOG_ERROR("Call to WSAIotcl(" << std::hex << SIO_RCVALL << ") failed with error code " << WSAGetLastError()); - closesocket(fd); - return false; - } - - m_Socket = new SocketContainer(); - ((SocketContainer*)m_Socket)->fd = fd; - - m_DeviceOpened = true; - - return true; - -#elif defined(__linux__) - -#if defined(__ANDROID_API__) && __ANDROID_API__ < 24 - PCPP_LOG_ERROR("Raw sockets aren't supported in Android API < 24"); - return false; -#else - if (!m_InterfaceIP.isValid()) - { - PCPP_LOG_ERROR("IP address is not valid"); - return false; - } - - int fd = socket(AF_PACKET, SOCK_RAW, htobe16(ETH_P_ALL)); - if (fd < 0) - { - PCPP_LOG_ERROR("Failed to create raw socket. Error code was " << errno); - return false; - } - - // find interface name and index from IP address - struct ifaddrs* addrs; - getifaddrs(&addrs); - std::string ifaceName = ""; - int ifaceIndex = -1; - for (struct ifaddrs* curAddr = addrs; curAddr != NULL; curAddr = curAddr->ifa_next) - { - if (curAddr->ifa_addr && (curAddr->ifa_flags & IFF_UP)) - { - if (curAddr->ifa_addr->sa_family == AF_INET) - { - struct sockaddr_in* sockAddr = (struct sockaddr_in*)(curAddr->ifa_addr); - char addrAsCharArr[32]; - inet_ntop(curAddr->ifa_addr->sa_family, (void *)&(sockAddr->sin_addr), addrAsCharArr, sizeof(addrAsCharArr)); - if (!strcmp(m_InterfaceIP.toString().c_str(), addrAsCharArr)) - { - ifaceName = curAddr->ifa_name; - ifaceIndex = if_nametoindex(curAddr->ifa_name); - } - } - else if (curAddr->ifa_addr->sa_family == AF_INET6) - { - struct sockaddr_in6* sockAddr = (struct sockaddr_in6*)(curAddr->ifa_addr); - char addrAsCharArr[40]; - inet_ntop(curAddr->ifa_addr->sa_family, (void *)&(sockAddr->sin6_addr), addrAsCharArr, sizeof(addrAsCharArr)); - if (!strcmp(m_InterfaceIP.toString().c_str(), addrAsCharArr)) - { - ifaceName = curAddr->ifa_name; - ifaceIndex = if_nametoindex(curAddr->ifa_name); - } - } - - } - } - freeifaddrs(addrs); - - if (ifaceName == "" || ifaceIndex < 0) - { - PCPP_LOG_ERROR("Cannot detect interface name or index from IP address"); - ::close(fd); - return false; - } - - // bind raw socket to interface - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifaceName.c_str()); - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1) - { - PCPP_LOG_ERROR("Cannot bind raw socket to interface '" << ifaceName << "'"); - ::close(fd); - return false; - } - - m_Socket = new SocketContainer(); - ((SocketContainer*)m_Socket)->fd = fd; - ((SocketContainer*)m_Socket)->interfaceIndex = ifaceIndex; - ((SocketContainer*)m_Socket)->interfaceName = ifaceName; - - m_DeviceOpened = true; - - return true; -#endif // __ANDROID_API__ - -#else - - PCPP_LOG_ERROR("Raw socket are not supported on this platform"); - return false; - -#endif -} - -void RawSocketDevice::close() -{ - if (m_Socket != nullptr && isOpened()) - { - SocketContainer* sockContainer = (SocketContainer*)m_Socket; -#if defined(_WIN32) - closesocket(sockContainer->fd); -#elif defined(__linux__) - ::close(sockContainer->fd); -#endif - delete sockContainer; - m_Socket = nullptr; - m_DeviceOpened = false; - } -} - -RawSocketDevice::RecvPacketResult RawSocketDevice::getError(int& errorCode) const -{ -#if defined(_WIN32) - errorCode = WSAGetLastError(); - if (errorCode == WSAEWOULDBLOCK) - return RecvWouldBlock; - if (errorCode == WSAETIMEDOUT) - return RecvTimeout; - - return RecvError; -#elif defined(__linux__) - if ((errorCode == EAGAIN) || (errorCode == EWOULDBLOCK)) - return RecvWouldBlock; - - return RecvError; -#else - return RecvError; -#endif -} - -} +#include "RawSocketDevice.h" +#include "EndianPortable.h" +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#include +#endif +#include +#include "Logger.h" +#include "IpUtils.h" +#include "SystemUtils.h" +#include "Packet.h" +#include "EthLayer.h" + +namespace pcpp +{ + +#define RAW_SOCKET_BUFFER_LEN 65536 + +#if defined(_WIN32) + +#ifndef SIO_RCVALL +/* SIO_RCVALL defined on w2k and later. Not defined in Mingw32 */ +/* 0x98000001 = _WSAIOW(IOC_VENDOR,1) */ +# define SIO_RCVALL 0x98000001 +#endif // SIO_RCVALL + +class WinSockInitializer +{ +private: + static bool m_IsInitialized; + +public: + + static void initialize() + { + if (m_IsInitialized) + return; + + // Load Winsock + WSADATA wsaData; + int res = WSAStartup(MAKEWORD(2,2), &wsaData); + if (res != 0) + { + PCPP_LOG_ERROR("WSAStartup failed with error code: " << res); + m_IsInitialized = false; + } + + m_IsInitialized = true; + } +}; + +bool WinSockInitializer::m_IsInitialized = false; + +#endif // defined(_WIN32) + +struct SocketContainer +{ +#if defined(_WIN32) + SOCKET fd; +#elif defined(__linux__) + int fd; + int interfaceIndex; + std::string interfaceName; +#endif +}; + +RawSocketDevice::RawSocketDevice(const IPAddress& interfaceIP) : IDevice(), m_Socket(nullptr) +{ +#if defined(_WIN32) + + WinSockInitializer::initialize(); + m_InterfaceIP = interfaceIP; + m_SockFamily = (m_InterfaceIP.getType() == IPAddress::IPv4AddressType ? IPv4 : IPv6); + +#elif defined(__linux__) + + m_InterfaceIP = interfaceIP; + m_SockFamily = Ethernet; + +#else + + m_SockFamily = Ethernet; + +#endif +} + + +RawSocketDevice::~RawSocketDevice() +{ + close(); +} + +RawSocketDevice::RecvPacketResult RawSocketDevice::receivePacket(RawPacket& rawPacket, bool blocking, int timeout) +{ +#if defined(_WIN32) + + if (!isOpened()) + { + PCPP_LOG_ERROR("Device is not open"); + return RecvError; + } + + SOCKET fd = ((SocketContainer*)m_Socket)->fd; + char* buffer = new char[RAW_SOCKET_BUFFER_LEN]; + memset(buffer, 0, RAW_SOCKET_BUFFER_LEN); + + // value of 0 timeout means disabling timeout + if (timeout < 0) + timeout = 0; + + u_long blockingMode = (blocking? 0 : 1); + ioctlsocket(fd, FIONBIO, &blockingMode); + + DWORD timeoutVal = timeout * 1000; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutVal, sizeof(timeoutVal)); + + //recvfrom(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0, (struct sockaddr*)&sockAddr,(socklen_t*)&sockAddrLen); + int bufferLen = recv(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0); + if (bufferLen < 0) + { + delete [] buffer; + int errorCode = 0; + RecvPacketResult error = getError(errorCode); + + if (error == RecvError) + PCPP_LOG_ERROR("Error reading from recvfrom. Error code is " << errorCode); + + return error; + } + + if (bufferLen > 0) + { + timeval time; + gettimeofday(&time, NULL); + rawPacket.setRawData((const uint8_t*)buffer, bufferLen, time, LINKTYPE_DLT_RAW1); + return RecvSuccess; + } + + PCPP_LOG_ERROR("Buffer length is zero"); + delete [] buffer; + return RecvError; + +#elif defined(__linux__) + + if (!isOpened()) + { + PCPP_LOG_ERROR("Device is not open"); + return RecvError; + } + + int fd = ((SocketContainer*)m_Socket)->fd; + char* buffer = new char[RAW_SOCKET_BUFFER_LEN]; + memset(buffer, 0, RAW_SOCKET_BUFFER_LEN); + + // value of 0 timeout means disabling timeout + if (timeout < 0) + timeout = 0; + + // set blocking or non-blocking flag + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) + { + delete [] buffer; + PCPP_LOG_ERROR("Cannot get socket flags"); + return RecvError; + } + flags = (blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)); + if (fcntl(fd, F_SETFL, flags) != 0) + { + delete [] buffer; + PCPP_LOG_ERROR("Cannot set socket non-blocking flag"); + return RecvError; + } + + // set timeout on socket + struct timeval timeoutVal; + timeoutVal.tv_sec = timeout; + timeoutVal.tv_usec = 0; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutVal, sizeof(timeoutVal)); + + int bufferLen = recv(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0); + if (bufferLen < 0) + { + delete [] buffer; + int errorCode = errno; + RecvPacketResult error = getError(errorCode); + + if (error == RecvError) + PCPP_LOG_ERROR("Error reading from recvfrom. Error code is " << errorCode); + + return error; + } + + if (bufferLen > 0) + { + timeval time; + gettimeofday(&time, NULL); + rawPacket.setRawData((const uint8_t*)buffer, bufferLen, time, LINKTYPE_ETHERNET); + return RecvSuccess; + } + + PCPP_LOG_ERROR("Buffer length is zero"); + delete [] buffer; + return RecvError; + +#else + + PCPP_LOG_ERROR("Raw socket are not supported on this platform"); + return RecvError; + +#endif +} + +int RawSocketDevice::receivePackets(RawPacketVector& packetVec, int timeout, int& failedRecv) +{ + if (!isOpened()) + { + PCPP_LOG_ERROR("Device is not open"); + return 0; + } + + long curSec, curNsec; + clockGetTime(curSec, curNsec); + + int packetCount = 0; + failedRecv = 0; + + long timeoutSec = curSec + timeout; + + while (curSec < timeoutSec) + { + RawPacket* rawPacket = new RawPacket(); + if (receivePacket(*rawPacket, true, timeoutSec-curSec) == RecvSuccess) + { + packetVec.pushBack(rawPacket); + packetCount++; + } + else + { + failedRecv++; + delete rawPacket; + } + + clockGetTime(curSec, curNsec); + } + + return packetCount; +} + +bool RawSocketDevice::sendPacket(const RawPacket* rawPacket) +{ +#if defined(_WIN32) + + PCPP_LOG_ERROR("Sending packets with raw socket are not supported on Windows"); + return 0; + +#elif defined(__linux__) + + if (!isOpened()) + { + PCPP_LOG_ERROR("Device is not open"); + return false; + } + + Packet packet((RawPacket*)rawPacket, OsiModelDataLinkLayer); + if (!packet.isPacketOfType(pcpp::Ethernet)) + { + PCPP_LOG_ERROR("Can't send non-Ethernet packets"); + return false; + } + + int fd = ((SocketContainer*)m_Socket)->fd; + + sockaddr_ll addr; + memset(&addr, 0, sizeof(struct sockaddr_ll)); + addr.sll_family = htobe16(PF_PACKET); + addr.sll_protocol = htobe16(ETH_P_ALL); + addr.sll_halen = 6; + addr.sll_ifindex = ((SocketContainer*)m_Socket)->interfaceIndex; + + EthLayer* ethLayer = packet.getLayerOfType(); + MacAddress dstMac = ethLayer->getDestMac(); + dstMac.copyTo((uint8_t*)&(addr.sll_addr)); + + if (::sendto(fd, ((RawPacket*)rawPacket)->getRawData(), ((RawPacket*)rawPacket)->getRawDataLen(), 0, (struct sockaddr*)&addr, sizeof(addr)) == -1) + { + PCPP_LOG_ERROR("Failed to send packet. Error was: '" << strerror(errno) << "'"); + return false; + } + + return true; + +#else + + PCPP_LOG_ERROR("Raw socket are not supported on this platform"); + return 0; + +#endif +} + +int RawSocketDevice::sendPackets(const RawPacketVector& packetVec) +{ +#if defined(_WIN32) + + PCPP_LOG_ERROR("Sending packets with raw socket are not supported on Windows"); + return false; + +#elif defined(__linux__) + + if (!isOpened()) + { + PCPP_LOG_ERROR("Device is not open"); + return 0; + } + + int fd = ((SocketContainer*)m_Socket)->fd; + + sockaddr_ll addr; + memset(&addr, 0, sizeof(struct sockaddr_ll)); + addr.sll_family = htobe16(PF_PACKET); + addr.sll_protocol = htobe16(ETH_P_ALL); + addr.sll_halen = 6; + addr.sll_ifindex = ((SocketContainer*)m_Socket)->interfaceIndex; + + int sendCount = 0; + + for (RawPacketVector::ConstVectorIterator iter = packetVec.begin(); iter != packetVec.end(); iter++) + { + Packet packet(*iter, OsiModelDataLinkLayer); + if (!packet.isPacketOfType(pcpp::Ethernet)) + { + PCPP_LOG_DEBUG("Can't send non-Ethernet packets"); + continue; + } + + EthLayer* ethLayer = packet.getLayerOfType(); + MacAddress dstMac = ethLayer->getDestMac(); + dstMac.copyTo((uint8_t*)&(addr.sll_addr)); + + if (::sendto(fd, (*iter)->getRawData(), (*iter)->getRawDataLen(), 0, (struct sockaddr*)&addr, sizeof(addr)) == -1) + { + PCPP_LOG_DEBUG("Failed to send packet. Error was: '" << strerror(errno) << "'"); + continue; + } + + sendCount++; + } + + return sendCount; + +#else + + PCPP_LOG_ERROR("Raw socket are not supported on this platform"); + return false; + +#endif +} + + +bool RawSocketDevice::open() +{ +#if defined(_WIN32) + + if (!m_InterfaceIP.isValid()) + { + PCPP_LOG_ERROR("IP address is not valid"); + return false; + } + + int family = (m_SockFamily == IPv4 ? AF_INET : AF_INET6); + SOCKET fd = socket(family, SOCK_RAW, IPPROTO_IP); + if ((int)fd == SOCKET_ERROR) + { + int error = WSAGetLastError(); + std::string additionalMessage = ""; + if (error == WSAEACCES) + additionalMessage = ", you may not be running with administrative privileges which is required for opening raw sockets on Windows"; + PCPP_LOG_ERROR("Failed to create raw socket. Error code was " << error << " " << additionalMessage); + return false; + } + + void* localAddr = NULL; + struct sockaddr_in localAddrIPv4; + struct sockaddr_in6 localAddrIPv6; + size_t localAddrSize = 0; + + if (m_SockFamily == IPv4) + { + localAddrIPv4.sin_family = family; + int res = inet_pton(family, m_InterfaceIP.toString().c_str(), &localAddrIPv4.sin_addr.s_addr); + if (res <= 0) + { + PCPP_LOG_ERROR("inet_pton failed, probably IP address provided is in bad format"); + closesocket(fd); + return false; + } + localAddrIPv4.sin_port = 0; // Any local port will do + localAddr = &localAddrIPv4; + localAddrSize = sizeof(localAddrIPv4); + } + else + { + localAddrIPv6.sin6_family = family; + int res = inet_pton(AF_INET6, m_InterfaceIP.toString().c_str(), &localAddrIPv6.sin6_addr.s6_addr); + if (res <= 0) + { + PCPP_LOG_ERROR("inet_pton failed, probably IP address provided is in bad format"); + closesocket(fd); + return false; + } + localAddrIPv6.sin6_port = 0; // Any local port will do + localAddrIPv6.sin6_scope_id = 0; + localAddr = &localAddrIPv6; + localAddrSize = sizeof(localAddrIPv6); + } + + if (bind(fd, (struct sockaddr *)localAddr, localAddrSize) == SOCKET_ERROR) + { + PCPP_LOG_ERROR("Failed to bind to interface. Error code was '" << WSAGetLastError() << "'"); + closesocket(fd); + return false; + } + + int n = 1; + DWORD dwBytesRet; + if (WSAIoctl(fd, SIO_RCVALL, &n, sizeof(n), NULL, 0, &dwBytesRet, NULL, NULL) == SOCKET_ERROR) + { + PCPP_LOG_ERROR("Call to WSAIotcl(" << std::hex << SIO_RCVALL << ") failed with error code " << WSAGetLastError()); + closesocket(fd); + return false; + } + + m_Socket = new SocketContainer(); + ((SocketContainer*)m_Socket)->fd = fd; + + m_DeviceOpened = true; + + return true; + +#elif defined(__linux__) + +#if defined(__ANDROID_API__) && __ANDROID_API__ < 24 + PCPP_LOG_ERROR("Raw sockets aren't supported in Android API < 24"); + return false; +#else + if (!m_InterfaceIP.isValid()) + { + PCPP_LOG_ERROR("IP address is not valid"); + return false; + } + + int fd = socket(AF_PACKET, SOCK_RAW, htobe16(ETH_P_ALL)); + if (fd < 0) + { + PCPP_LOG_ERROR("Failed to create raw socket. Error code was " << errno); + return false; + } + + // find interface name and index from IP address + struct ifaddrs* addrs; + getifaddrs(&addrs); + std::string ifaceName = ""; + int ifaceIndex = -1; + for (struct ifaddrs* curAddr = addrs; curAddr != NULL; curAddr = curAddr->ifa_next) + { + if (curAddr->ifa_addr && (curAddr->ifa_flags & IFF_UP)) + { + if (curAddr->ifa_addr->sa_family == AF_INET) + { + struct sockaddr_in* sockAddr = (struct sockaddr_in*)(curAddr->ifa_addr); + char addrAsCharArr[32]; + inet_ntop(curAddr->ifa_addr->sa_family, (void *)&(sockAddr->sin_addr), addrAsCharArr, sizeof(addrAsCharArr)); + if (!strcmp(m_InterfaceIP.toString().c_str(), addrAsCharArr)) + { + ifaceName = curAddr->ifa_name; + ifaceIndex = if_nametoindex(curAddr->ifa_name); + } + } + else if (curAddr->ifa_addr->sa_family == AF_INET6) + { + struct sockaddr_in6* sockAddr = (struct sockaddr_in6*)(curAddr->ifa_addr); + char addrAsCharArr[40]; + inet_ntop(curAddr->ifa_addr->sa_family, (void *)&(sockAddr->sin6_addr), addrAsCharArr, sizeof(addrAsCharArr)); + if (!strcmp(m_InterfaceIP.toString().c_str(), addrAsCharArr)) + { + ifaceName = curAddr->ifa_name; + ifaceIndex = if_nametoindex(curAddr->ifa_name); + } + } + + } + } + freeifaddrs(addrs); + + if (ifaceName == "" || ifaceIndex < 0) + { + PCPP_LOG_ERROR("Cannot detect interface name or index from IP address"); + ::close(fd); + return false; + } + + // bind raw socket to interface + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifaceName.c_str()); + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1) + { + PCPP_LOG_ERROR("Cannot bind raw socket to interface '" << ifaceName << "'"); + ::close(fd); + return false; + } + + m_Socket = new SocketContainer(); + ((SocketContainer*)m_Socket)->fd = fd; + ((SocketContainer*)m_Socket)->interfaceIndex = ifaceIndex; + ((SocketContainer*)m_Socket)->interfaceName = ifaceName; + + m_DeviceOpened = true; + + return true; +#endif // __ANDROID_API__ + +#else + + PCPP_LOG_ERROR("Raw socket are not supported on this platform"); + return false; + +#endif +} + +void RawSocketDevice::close() +{ + if (m_Socket != nullptr && isOpened()) + { + SocketContainer* sockContainer = (SocketContainer*)m_Socket; +#if defined(_WIN32) + closesocket(sockContainer->fd); +#elif defined(__linux__) + ::close(sockContainer->fd); +#endif + delete sockContainer; + m_Socket = nullptr; + m_DeviceOpened = false; + } +} + +RawSocketDevice::RecvPacketResult RawSocketDevice::getError(int& errorCode) const +{ +#if defined(_WIN32) + errorCode = WSAGetLastError(); + if (errorCode == WSAEWOULDBLOCK) + return RecvWouldBlock; + if (errorCode == WSAETIMEDOUT) + return RecvTimeout; + + return RecvError; +#elif defined(__linux__) + if ((errorCode == EAGAIN) || (errorCode == EWOULDBLOCK)) + return RecvWouldBlock; + + return RecvError; +#else + return RecvError; +#endif +} + +} diff --git a/Pcap++/src/WinPcapLiveDevice.cpp b/Pcap++/src/WinPcapLiveDevice.cpp index b43f262841..c1e0429c7e 100644 --- a/Pcap++/src/WinPcapLiveDevice.cpp +++ b/Pcap++/src/WinPcapLiveDevice.cpp @@ -1,131 +1,131 @@ -#if defined(_WIN32) - -#define LOG_MODULE PcapLogModuleWinPcapLiveDevice - -#include "WinPcapLiveDevice.h" -#include "Logger.h" -#include "TimespecTimeval.h" -#include "pcap.h" - -namespace pcpp -{ - -WinPcapLiveDevice::WinPcapLiveDevice(pcap_if_t* iface, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway) : PcapLiveDevice(iface, calculateMTU, calculateMacAddress, calculateDefaultGateway) -{ - m_MinAmountOfDataToCopyFromKernelToApplication = 16000; -} - -bool WinPcapLiveDevice::startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie, int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie) -{ - if (!m_DeviceOpened || m_PcapDescriptor == NULL) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); - return false; - } - - //Put the interface in capture mode - if (pcap_setmode(m_PcapDescriptor, MODE_CAPT) < 0) - { - PCPP_LOG_ERROR("Error setting the capture mode for device '" << m_Name << "'"); - return false; - } - - return PcapLiveDevice::startCapture(onPacketArrives, onPacketArrivesUserCookie, intervalInSecondsToUpdateStats, onStatsUpdate, onStatsUpdateUserCookie); -} - -bool WinPcapLiveDevice::startCapture(int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie) -{ - if (!m_DeviceOpened || m_PcapDescriptor == NULL) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); - return false; - } - - //Put the interface in statistics mode - if (pcap_setmode(m_PcapDescriptor, MODE_STAT) < 0) - { - PCPP_LOG_ERROR("Error setting the statistics mode for device '" << m_Name << "'"); - return false; - } - - return PcapLiveDevice::startCapture(intervalInSecondsToUpdateStats, onStatsUpdate, onStatsUpdateUserCookie); -} - -int WinPcapLiveDevice::sendPackets(RawPacket* rawPacketsArr, int arrLength) -{ - if (!m_DeviceOpened || m_PcapDescriptor == NULL) - { - PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); - return 0; - } - - int dataSize = 0; - int packetsSent = 0; - for (int i = 0; i < arrLength; i++) - dataSize += rawPacketsArr[i].getRawDataLen(); - - pcap_send_queue* sendQueue = pcap_sendqueue_alloc(dataSize + arrLength*sizeof(pcap_pkthdr)); - PCPP_LOG_DEBUG("Allocated send queue of size " << (dataSize + arrLength*sizeof(pcap_pkthdr))); - struct pcap_pkthdr* packetHeader = new struct pcap_pkthdr[arrLength]; - for (int i = 0; i < arrLength; i++) - { - packetHeader[i].caplen = rawPacketsArr[i].getRawDataLen(); - packetHeader[i].len = rawPacketsArr[i].getRawDataLen(); - timespec packet_time = rawPacketsArr[i].getPacketTimeStamp(); - TIMESPEC_TO_TIMEVAL(&packetHeader[i].ts, &packet_time); - if (pcap_sendqueue_queue(sendQueue, &packetHeader[i], rawPacketsArr[i].getRawData()) == -1) - { - PCPP_LOG_ERROR("pcap_send_queue is too small for all packets. Sending only " << i << " packets"); - break; - } - packetsSent++; - } - - PCPP_LOG_DEBUG(packetsSent << " packets were queued successfully"); - - int res; - if ((res = pcap_sendqueue_transmit(m_PcapDescriptor, sendQueue, 0)) < (int)(sendQueue->len)) - { - PCPP_LOG_ERROR("An error occurred sending the packets: " << pcap_geterr(m_PcapDescriptor) << ". Only " << res << " bytes were sent"); - packetsSent = 0; - dataSize = 0; - for (int i = 0; i < arrLength; i++) - { - dataSize += rawPacketsArr[i].getRawDataLen(); - if (dataSize > res) - { - return packetsSent; - } - packetsSent++; - } - return packetsSent; - } - PCPP_LOG_DEBUG("Packets were sent successfully"); - - pcap_sendqueue_destroy(sendQueue); - PCPP_LOG_DEBUG("Send queue destroyed"); - - delete[] packetHeader; - return packetsSent; -} - -bool WinPcapLiveDevice::setMinAmountOfDataToCopyFromKernelToApplication(int size) -{ - if (!m_DeviceOpened) - { - PCPP_LOG_ERROR("Device not opened"); - return false; - } - - if (pcap_setmintocopy(m_PcapDescriptor, size) != 0) - { - PCPP_LOG_ERROR("pcap_setmintocopy failed"); - return false; - } - m_MinAmountOfDataToCopyFromKernelToApplication = size; - return true; -} - -} // namespace pcpp - -#endif // _WIN32 +#if defined(_WIN32) + +#define LOG_MODULE PcapLogModuleWinPcapLiveDevice + +#include "WinPcapLiveDevice.h" +#include "Logger.h" +#include "TimespecTimeval.h" +#include "pcap.h" + +namespace pcpp +{ + +WinPcapLiveDevice::WinPcapLiveDevice(pcap_if_t* iface, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway) : PcapLiveDevice(iface, calculateMTU, calculateMacAddress, calculateDefaultGateway) +{ + m_MinAmountOfDataToCopyFromKernelToApplication = 16000; +} + +bool WinPcapLiveDevice::startCapture(OnPacketArrivesCallback onPacketArrives, void* onPacketArrivesUserCookie, int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie) +{ + if (!m_DeviceOpened || m_PcapDescriptor == NULL) + { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); + return false; + } + + //Put the interface in capture mode + if (pcap_setmode(m_PcapDescriptor, MODE_CAPT) < 0) + { + PCPP_LOG_ERROR("Error setting the capture mode for device '" << m_Name << "'"); + return false; + } + + return PcapLiveDevice::startCapture(onPacketArrives, onPacketArrivesUserCookie, intervalInSecondsToUpdateStats, onStatsUpdate, onStatsUpdateUserCookie); +} + +bool WinPcapLiveDevice::startCapture(int intervalInSecondsToUpdateStats, OnStatsUpdateCallback onStatsUpdate, void* onStatsUpdateUserCookie) +{ + if (!m_DeviceOpened || m_PcapDescriptor == NULL) + { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); + return false; + } + + //Put the interface in statistics mode + if (pcap_setmode(m_PcapDescriptor, MODE_STAT) < 0) + { + PCPP_LOG_ERROR("Error setting the statistics mode for device '" << m_Name << "'"); + return false; + } + + return PcapLiveDevice::startCapture(intervalInSecondsToUpdateStats, onStatsUpdate, onStatsUpdateUserCookie); +} + +int WinPcapLiveDevice::sendPackets(RawPacket* rawPacketsArr, int arrLength) +{ + if (!m_DeviceOpened || m_PcapDescriptor == NULL) + { + PCPP_LOG_ERROR("Device '" << m_Name << "' not opened"); + return 0; + } + + int dataSize = 0; + int packetsSent = 0; + for (int i = 0; i < arrLength; i++) + dataSize += rawPacketsArr[i].getRawDataLen(); + + pcap_send_queue* sendQueue = pcap_sendqueue_alloc(dataSize + arrLength*sizeof(pcap_pkthdr)); + PCPP_LOG_DEBUG("Allocated send queue of size " << (dataSize + arrLength*sizeof(pcap_pkthdr))); + struct pcap_pkthdr* packetHeader = new struct pcap_pkthdr[arrLength]; + for (int i = 0; i < arrLength; i++) + { + packetHeader[i].caplen = rawPacketsArr[i].getRawDataLen(); + packetHeader[i].len = rawPacketsArr[i].getRawDataLen(); + timespec packet_time = rawPacketsArr[i].getPacketTimeStamp(); + TIMESPEC_TO_TIMEVAL(&packetHeader[i].ts, &packet_time); + if (pcap_sendqueue_queue(sendQueue, &packetHeader[i], rawPacketsArr[i].getRawData()) == -1) + { + PCPP_LOG_ERROR("pcap_send_queue is too small for all packets. Sending only " << i << " packets"); + break; + } + packetsSent++; + } + + PCPP_LOG_DEBUG(packetsSent << " packets were queued successfully"); + + int res; + if ((res = pcap_sendqueue_transmit(m_PcapDescriptor, sendQueue, 0)) < (int)(sendQueue->len)) + { + PCPP_LOG_ERROR("An error occurred sending the packets: " << pcap_geterr(m_PcapDescriptor) << ". Only " << res << " bytes were sent"); + packetsSent = 0; + dataSize = 0; + for (int i = 0; i < arrLength; i++) + { + dataSize += rawPacketsArr[i].getRawDataLen(); + if (dataSize > res) + { + return packetsSent; + } + packetsSent++; + } + return packetsSent; + } + PCPP_LOG_DEBUG("Packets were sent successfully"); + + pcap_sendqueue_destroy(sendQueue); + PCPP_LOG_DEBUG("Send queue destroyed"); + + delete[] packetHeader; + return packetsSent; +} + +bool WinPcapLiveDevice::setMinAmountOfDataToCopyFromKernelToApplication(int size) +{ + if (!m_DeviceOpened) + { + PCPP_LOG_ERROR("Device not opened"); + return false; + } + + if (pcap_setmintocopy(m_PcapDescriptor, size) != 0) + { + PCPP_LOG_ERROR("pcap_setmintocopy failed"); + return false; + } + m_MinAmountOfDataToCopyFromKernelToApplication = size; + return true; +} + +} // namespace pcpp + +#endif // _WIN32 diff --git a/README.md b/README.md index a19490a784..73b8c19552 100644 --- a/README.md +++ b/README.md @@ -1,296 +1,296 @@ - -
- -[![PcapPlusPlus Logo](https://pcapplusplus.github.io/img/logo/logo_color.png)](https://pcapplusplus.github.io) - -[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/seladb/PcapPlusPlus/build_and_test.yml?branch=master&label=Actions&logo=github&style=flat)](https://github.com/seladb/PcapPlusPlus/actions?query=workflow%3A%22Build+and+test%22) -[![Cirrus CI - Base Branch Build Status](https://img.shields.io/cirrus/github/seladb/PcapPlusPlus?label=Cirrus%20CI&logo=cirrusci&style=flat)](https://cirrus-ci.com/github/seladb/PcapPlusPlus) -[![AppVeyor](https://img.shields.io/appveyor/build/seladb/PcapPlusPlus/master?label=AppVeyor&logo=appveyor&logoColor=white&style=flat)](https://ci.appveyor.com/project/seladb/pcapplusplus/branch/master) -[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/seladb/PcapPlusPlus/codeql.yml?branch=master&label=CodeQL&logo=github&style=flat)](https://github.com/seladb/PcapPlusPlus/actions?query=workflow%3A%22CodeQL%22) -[![GitHub contributors](https://img.shields.io/github/contributors/seladb/PcapPlusPlus?style=flat&label=Contributors&logo=github)](https://github.com/seladb/PcapPlusPlus/graphs/contributors) -
-[![Twitter Follow](https://img.shields.io/badge/follow-%40seladb-1DA1F2?logo=twitter&style=social)](https://twitter.com/intent/follow?screen_name=seladb) -[![GitHub Repo stars](https://img.shields.io/github/stars/seladb/PcapPlusPlus?style=social)]() - -
- -[PcapPlusPlus](https://pcapplusplus.github.io/) is a multiplatform C++ library for capturing, parsing and crafting of network packets. It is designed to be efficient, powerful and easy to use. - -PcapPlusPlus enables decoding and forging capabilities for a large variety of network protocols. It also provides easy to use C++ wrappers for the most popular packet processing engines such as [libpcap](https://www.tcpdump.org/), [WinPcap](https://www.winpcap.org/), [Npcap](https://nmap.org/npcap/), [DPDK](https://www.dpdk.org/) and [PF_RING](https://www.ntop.org/products/packet-capture/pf_ring/). - -## Table Of Contents - -- [Table Of Contents](#table-of-contents) -- [Download](#download) - - [GitHub Release Page](#github-release-page) - - [Homebrew](#homebrew) - - [Vcpkg](#vcpkg) - - [Conan](#conan) - - [Build It Yourself](#build-it-yourself) -- [Feature Overview](#feature-overview) -- [Getting Started](#getting-started) -- [API Documentation](#api-documentation) -- [Multi Platform Support](#multi-platform-support) -- [Supported Network Protocols](#supported-network-protocols) - - [Data Link Layer (L2)](#data-link-layer-l2) - - [Network Layer (L3)](#network-layer-l3) - - [Transport Layer (L4)](#transport-layer-l4) - - [Session Layer (L5)](#session-layer-l5) - - [Presentation Layer (L6)](#presentation-layer-l6) - - [Application Layer (L7)](#application-layer-l7) -- [DPDK And PF_RING Support](#dpdk-and-pf_ring-support) -- [Benchmarks](#benchmarks) -- [Provide Feedback](#provide-feedback) -- [Contributing](#contributing) -- [License](#license) - -## Download - -You can choose between downloading from GitHub release page, use a package manager or build PcapPlusPlus yourself. For more details please visit the [Download](https://pcapplusplus.github.io/docs/install) page in PcapPlusPlus web-site. - -[![GitHub all releases](https://img.shields.io/github/downloads/seladb/PcapPlusPlus/total?style=flat&label=Downloads&logo=github)]() - -### GitHub Release Page - - - -### Homebrew - -```shell -brew install pcapplusplus -``` - -Homebrew formulae: - -### Vcpkg - -Windows: - -```text -.\vcpkg install pcapplusplus -``` - -MacOS/Linux: - -```text -vcpkg install pcapplusplus -``` - -Vcpkg port: - -### Conan - -```text -conan install "pcapplusplus/[>0]@" -u -``` - -The package in ConanCenter: - -### Build It Yourself - -Clone the git repository: - -```shell -git clone https://github.com/seladb/PcapPlusPlus.git -``` - -Follow the build instructions according to your platform in the [Build From Source](https://pcapplusplus.github.io/docs/install#build-from-source) page in PcapPlusPlus web-site. - -## Feature Overview - -- __Packet capture__ through an easy to use C++ wrapper for popular packet capture engines such as [libpcap](https://www.tcpdump.org/), [WinPcap](https://www.winpcap.org/), [Npcap](https://nmap.org/npcap/), [Intel DPDK](https://www.dpdk.org/), [ntop’s PF_RING](https://www.ntop.org/products/packet-capture/pf_ring/) and [raw sockets](https://en.wikipedia.org/wiki/Network_socket#Raw_socket) [[Learn more](https://pcapplusplus.github.io/docs/features#packet-capture)] -- __Packet parsing and crafting__ including detailed analysis of protocols and layers, packet generation and packet edit for a large variety of [network protocols](https://pcapplusplus.github.io/docs/features#supported-network-protocols) [[Learn more](https://pcapplusplus.github.io/docs/features#packet-parsing-and-crafting)] -- __Read and write packets from/to files__ in both __PCAP__ and __PCAPNG__ formats [[Learn more](https://pcapplusplus.github.io/docs/features#read-and-write-packets-fromto-files)] -- __Packet processing in line rate__ through an efficient and easy to use C++ wrapper for [DPDK](https://www.dpdk.org/) and [PF_RING](https://www.ntop.org/products/packet-capture/pf_ring/) [[Learn more](https://pcapplusplus.github.io/docs/features#dpdk-support)] -- __Multiplatform support__ - PcapPlusPlus is fully supported on Linux, MacOS, Windows, Android and FreeBSD -- __Packet reassembly__ - unique implementation of __TCP Reassembly__ which includes TCP retransmission, out-of-order TCP packets and missing TCP data, and __IP Fragmentation and Defragmentation__ to create and reassemble IPv4 and IPv6 fragments [[Learn more](https://pcapplusplus.github.io/docs/features#packet-reassembly)] -- __Packet filtering__ that makes libpcap's BPF filters a lot more user-friendly [[Learn more](https://pcapplusplus.github.io/docs/features#packet-filtering)] -- __TLS Fingerprinting__ - a C++ implementation of [JA3 and JA3S](https://github.com/salesforce/ja3) TLS fingerprinting [[Learn more](https://pcapplusplus.github.io/docs/features#tls-fingerprinting)] - -## Getting Started - -Writing applications with PcapPlusPlus is very easy and intuitive. Here is a simple application that shows how to read a packet from a PCAP file and parse it: - -```cpp -#include -#include "IPv4Layer.h" -#include "Packet.h" -#include "PcapFileDevice.h" - -int main(int argc, char* argv[]) -{ - // open a pcap file for reading - pcpp::PcapFileReaderDevice reader("1_packet.pcap"); - if (!reader.open()) - { - std::cerr << "Error opening the pcap file" << std::endl; - return 1; - } - - // read the first (and only) packet from the file - pcpp::RawPacket rawPacket; - if (!reader.getNextPacket(rawPacket)) - { - std::cerr << "Couldn't read the first packet in the file" << std::endl; - return 1; - } - - // parse the raw packet into a parsed packet - pcpp::Packet parsedPacket(&rawPacket); - - // verify the packet is IPv4 - if (parsedPacket.isPacketOfType(pcpp::IPv4)) - { - // extract source and dest IPs - pcpp::IPv4Address srcIP = parsedPacket.getLayerOfType()->getSrcIPv4Address(); - pcpp::IPv4Address destIP = parsedPacket.getLayerOfType()->getDstIPv4Address(); - - // print source and dest IPs - std::cout << "Source IP is '" << srcIP << "'; Dest IP is '" << destIP << "'" << std::endl; - } - - // close the file - reader.close(); - - return 0; -} -``` - -You can find much more information in the [Getting Started](https://pcapplusplus.github.io/docs/quickstart) page in PcapPlusPlus web-site. This page will walk you through few easy steps to have an app up and running. - -## API Documentation - -PcapPlusPlus consists of 3 libraries: - -1. __Packet++__ - a library for parsing, creating and editing network packets -2. __Pcap++__ - a library for intercepting and sending packets, providing network and NIC info, stats, etc. It is actually a C++ wrapper for packet capturing engines such as libpcap, WinPcap, Npcap, DPDK and PF_RING -3. __Common++__ - a library with some common code utilities used by both Packet++ and Pcap++ - -You can find an extensive API documentation in the [API documentation section](https://pcapplusplus.github.io/docs/api) in PcapPlusPlus web-site. -If you see any missing data please [contact us](#provide-feedback). - -## Multi Platform Support - -PcapPlusPlus is currently supported on -__Windows__ - - -, -__Linux__ - - -, -__MacOS__ - - -, -__Android__ - - - and -__FreeBSD__ - - -. -Please visit PcapPlusPlus web-site to see all of the [supported platforms](https://pcapplusplus.github.io/docs/platforms) and refer to the [Download](#download) section to start using PcapPlusPlus on your platform. - -## Supported Network Protocols - -PcapPlusPlus currently supports parsing, editing and creation of packets of the following protocols: - -### Data Link Layer (L2) - -1. Ethernet II -2. IEEE 802.3 Ethernet -3. LLC (Only BPDU supported) -4. Null/Loopback -5. Packet trailer (a.k.a footer or padding) -6. PPPoE -7. SLL (Linux cooked capture) -8. STP -9. VLAN -10. VXLAN -11. Wake on LAN (WoL) - -### Network Layer (L3) - -12. ARP -13. GRE -14. ICMP -15. ICMPv6 -16. IGMP (IGMPv1, IGMPv2 and IGMPv3 are supported) -17. IPv4 -18. IPv6 -19. MPLS -20. NDP -21. Raw IP (IPv4 & IPv6) - -### Transport Layer (L4) - -22. GTP (v1) -23. IPSec AH & ESP - parsing only (no editing capabilities) -24. TCP -25. UDP - -### Session Layer (L5) - -26. SDP -27. SIP - -### Presentation Layer (L6) - -28. SSL/TLS - parsing only (no editing capabilities) - -### Application Layer (L7) - -29. BGP (v4) -30. DHCP -31. DHCPv6 -32. DNS -33. FTP -34. HTTP headers (request & response) -35. NTP (v3, v4) -36. Radius -37. SOME/IP -38. SSH - parsing only (no editing capabilities) -39. Telnet - parsing only (no editing capabilities) -40. Generic payload - -## DPDK And PF_RING Support - -[The Data Plane Development Kit (DPDK)](https://www.dpdk.org/) is a set of data plane libraries and network interface controller drivers for fast packet processing. - -[PF_RING™](https://www.ntop.org/products/packet-capture/pf_ring/) is a new type of network socket that dramatically improves the packet capture speed. - -Both frameworks provide very fast packets processing (up to line speed) and are used in many network applications such as routers, firewalls, load balancers, etc. -PcapPlusPLus provides a C++ abstraction layer over DPDK & PF_RING. This abstraction layer provides an easy to use interface that removes a lot of the boilerplate involved in using these frameworks. You can learn more by visiting the [DPDK](https://pcapplusplus.github.io/docs/dpdk) & [PF_RING](https://pcapplusplus.github.io/docs/features#pf_ring-support) support pages in PcapPlusPlus web-site. - -## Benchmarks - -We used Matias Fontanini's [packet-capture-benchmarks](https://github.com/mfontanini/packet-capture-benchmarks) project to compare the performance of PcapPlusPlus with other similar C++ libraries (such as `libtins` and `libcrafter`). - -You can see the results in the [Benchmarks](https://pcapplusplus.github.io/docs/benchmark) page in PcapPlusPlus web-site. - -## Provide Feedback - -We'd be more than happy to get feedback, please feel free to reach out to us in any of the following ways: - -- Open a GitHub ticket -- Post a message in PcapPlusPlus Google group: -- Ask a question on Stack Overflow: -- Send an email to: -- Follow us on Twitter: - -If you like this project please __Star us on GitHub — it helps!__ :star: :star: - -Please visit the [PcapPlusPlus web-site](https://pcapplusplus.github.io/community) to learn more. - -## Contributing - -We would very much appreciate any contribution to this project. If you're interested in contributing please visit the [contribution page](https://pcapplusplus.github.io/community#contribute) in PcapPlusPlus web-site. - -## License - -PcapPlusPlus is released under the [Unlicense license](https://unlicense.org/). - -[![GitHub](https://img.shields.io/github/license/seladb/PcapPlusPlus?style=flat&color=blue&logo=unlicense)](https://unlicense.org/) + +
+ +[![PcapPlusPlus Logo](https://pcapplusplus.github.io/img/logo/logo_color.png)](https://pcapplusplus.github.io) + +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/seladb/PcapPlusPlus/build_and_test.yml?branch=master&label=Actions&logo=github&style=flat)](https://github.com/seladb/PcapPlusPlus/actions?query=workflow%3A%22Build+and+test%22) +[![Cirrus CI - Base Branch Build Status](https://img.shields.io/cirrus/github/seladb/PcapPlusPlus?label=Cirrus%20CI&logo=cirrusci&style=flat)](https://cirrus-ci.com/github/seladb/PcapPlusPlus) +[![AppVeyor](https://img.shields.io/appveyor/build/seladb/PcapPlusPlus/master?label=AppVeyor&logo=appveyor&logoColor=white&style=flat)](https://ci.appveyor.com/project/seladb/pcapplusplus/branch/master) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/seladb/PcapPlusPlus/codeql.yml?branch=master&label=CodeQL&logo=github&style=flat)](https://github.com/seladb/PcapPlusPlus/actions?query=workflow%3A%22CodeQL%22) +[![GitHub contributors](https://img.shields.io/github/contributors/seladb/PcapPlusPlus?style=flat&label=Contributors&logo=github)](https://github.com/seladb/PcapPlusPlus/graphs/contributors) +
+[![Twitter Follow](https://img.shields.io/badge/follow-%40seladb-1DA1F2?logo=twitter&style=social)](https://twitter.com/intent/follow?screen_name=seladb) +[![GitHub Repo stars](https://img.shields.io/github/stars/seladb/PcapPlusPlus?style=social)]() + +
+ +[PcapPlusPlus](https://pcapplusplus.github.io/) is a multiplatform C++ library for capturing, parsing and crafting of network packets. It is designed to be efficient, powerful and easy to use. + +PcapPlusPlus enables decoding and forging capabilities for a large variety of network protocols. It also provides easy to use C++ wrappers for the most popular packet processing engines such as [libpcap](https://www.tcpdump.org/), [WinPcap](https://www.winpcap.org/), [Npcap](https://nmap.org/npcap/), [DPDK](https://www.dpdk.org/) and [PF_RING](https://www.ntop.org/products/packet-capture/pf_ring/). + +## Table Of Contents + +- [Table Of Contents](#table-of-contents) +- [Download](#download) + - [GitHub Release Page](#github-release-page) + - [Homebrew](#homebrew) + - [Vcpkg](#vcpkg) + - [Conan](#conan) + - [Build It Yourself](#build-it-yourself) +- [Feature Overview](#feature-overview) +- [Getting Started](#getting-started) +- [API Documentation](#api-documentation) +- [Multi Platform Support](#multi-platform-support) +- [Supported Network Protocols](#supported-network-protocols) + - [Data Link Layer (L2)](#data-link-layer-l2) + - [Network Layer (L3)](#network-layer-l3) + - [Transport Layer (L4)](#transport-layer-l4) + - [Session Layer (L5)](#session-layer-l5) + - [Presentation Layer (L6)](#presentation-layer-l6) + - [Application Layer (L7)](#application-layer-l7) +- [DPDK And PF_RING Support](#dpdk-and-pf_ring-support) +- [Benchmarks](#benchmarks) +- [Provide Feedback](#provide-feedback) +- [Contributing](#contributing) +- [License](#license) + +## Download + +You can choose between downloading from GitHub release page, use a package manager or build PcapPlusPlus yourself. For more details please visit the [Download](https://pcapplusplus.github.io/docs/install) page in PcapPlusPlus web-site. + +[![GitHub all releases](https://img.shields.io/github/downloads/seladb/PcapPlusPlus/total?style=flat&label=Downloads&logo=github)]() + +### GitHub Release Page + + + +### Homebrew + +```shell +brew install pcapplusplus +``` + +Homebrew formulae: + +### Vcpkg + +Windows: + +```text +.\vcpkg install pcapplusplus +``` + +MacOS/Linux: + +```text +vcpkg install pcapplusplus +``` + +Vcpkg port: + +### Conan + +```text +conan install "pcapplusplus/[>0]@" -u +``` + +The package in ConanCenter: + +### Build It Yourself + +Clone the git repository: + +```shell +git clone https://github.com/seladb/PcapPlusPlus.git +``` + +Follow the build instructions according to your platform in the [Build From Source](https://pcapplusplus.github.io/docs/install#build-from-source) page in PcapPlusPlus web-site. + +## Feature Overview + +- __Packet capture__ through an easy to use C++ wrapper for popular packet capture engines such as [libpcap](https://www.tcpdump.org/), [WinPcap](https://www.winpcap.org/), [Npcap](https://nmap.org/npcap/), [Intel DPDK](https://www.dpdk.org/), [ntop’s PF_RING](https://www.ntop.org/products/packet-capture/pf_ring/) and [raw sockets](https://en.wikipedia.org/wiki/Network_socket#Raw_socket) [[Learn more](https://pcapplusplus.github.io/docs/features#packet-capture)] +- __Packet parsing and crafting__ including detailed analysis of protocols and layers, packet generation and packet edit for a large variety of [network protocols](https://pcapplusplus.github.io/docs/features#supported-network-protocols) [[Learn more](https://pcapplusplus.github.io/docs/features#packet-parsing-and-crafting)] +- __Read and write packets from/to files__ in both __PCAP__ and __PCAPNG__ formats [[Learn more](https://pcapplusplus.github.io/docs/features#read-and-write-packets-fromto-files)] +- __Packet processing in line rate__ through an efficient and easy to use C++ wrapper for [DPDK](https://www.dpdk.org/) and [PF_RING](https://www.ntop.org/products/packet-capture/pf_ring/) [[Learn more](https://pcapplusplus.github.io/docs/features#dpdk-support)] +- __Multiplatform support__ - PcapPlusPlus is fully supported on Linux, MacOS, Windows, Android and FreeBSD +- __Packet reassembly__ - unique implementation of __TCP Reassembly__ which includes TCP retransmission, out-of-order TCP packets and missing TCP data, and __IP Fragmentation and Defragmentation__ to create and reassemble IPv4 and IPv6 fragments [[Learn more](https://pcapplusplus.github.io/docs/features#packet-reassembly)] +- __Packet filtering__ that makes libpcap's BPF filters a lot more user-friendly [[Learn more](https://pcapplusplus.github.io/docs/features#packet-filtering)] +- __TLS Fingerprinting__ - a C++ implementation of [JA3 and JA3S](https://github.com/salesforce/ja3) TLS fingerprinting [[Learn more](https://pcapplusplus.github.io/docs/features#tls-fingerprinting)] + +## Getting Started + +Writing applications with PcapPlusPlus is very easy and intuitive. Here is a simple application that shows how to read a packet from a PCAP file and parse it: + +```cpp +#include +#include "IPv4Layer.h" +#include "Packet.h" +#include "PcapFileDevice.h" + +int main(int argc, char* argv[]) +{ + // open a pcap file for reading + pcpp::PcapFileReaderDevice reader("1_packet.pcap"); + if (!reader.open()) + { + std::cerr << "Error opening the pcap file" << std::endl; + return 1; + } + + // read the first (and only) packet from the file + pcpp::RawPacket rawPacket; + if (!reader.getNextPacket(rawPacket)) + { + std::cerr << "Couldn't read the first packet in the file" << std::endl; + return 1; + } + + // parse the raw packet into a parsed packet + pcpp::Packet parsedPacket(&rawPacket); + + // verify the packet is IPv4 + if (parsedPacket.isPacketOfType(pcpp::IPv4)) + { + // extract source and dest IPs + pcpp::IPv4Address srcIP = parsedPacket.getLayerOfType()->getSrcIPv4Address(); + pcpp::IPv4Address destIP = parsedPacket.getLayerOfType()->getDstIPv4Address(); + + // print source and dest IPs + std::cout << "Source IP is '" << srcIP << "'; Dest IP is '" << destIP << "'" << std::endl; + } + + // close the file + reader.close(); + + return 0; +} +``` + +You can find much more information in the [Getting Started](https://pcapplusplus.github.io/docs/quickstart) page in PcapPlusPlus web-site. This page will walk you through few easy steps to have an app up and running. + +## API Documentation + +PcapPlusPlus consists of 3 libraries: + +1. __Packet++__ - a library for parsing, creating and editing network packets +2. __Pcap++__ - a library for intercepting and sending packets, providing network and NIC info, stats, etc. It is actually a C++ wrapper for packet capturing engines such as libpcap, WinPcap, Npcap, DPDK and PF_RING +3. __Common++__ - a library with some common code utilities used by both Packet++ and Pcap++ + +You can find an extensive API documentation in the [API documentation section](https://pcapplusplus.github.io/docs/api) in PcapPlusPlus web-site. +If you see any missing data please [contact us](#provide-feedback). + +## Multi Platform Support + +PcapPlusPlus is currently supported on +__Windows__ + + +, +__Linux__ + + +, +__MacOS__ + + +, +__Android__ + + + and +__FreeBSD__ + + +. +Please visit PcapPlusPlus web-site to see all of the [supported platforms](https://pcapplusplus.github.io/docs/platforms) and refer to the [Download](#download) section to start using PcapPlusPlus on your platform. + +## Supported Network Protocols + +PcapPlusPlus currently supports parsing, editing and creation of packets of the following protocols: + +### Data Link Layer (L2) + +1. Ethernet II +2. IEEE 802.3 Ethernet +3. LLC (Only BPDU supported) +4. Null/Loopback +5. Packet trailer (a.k.a footer or padding) +6. PPPoE +7. SLL (Linux cooked capture) +8. STP +9. VLAN +10. VXLAN +11. Wake on LAN (WoL) + +### Network Layer (L3) + +12. ARP +13. GRE +14. ICMP +15. ICMPv6 +16. IGMP (IGMPv1, IGMPv2 and IGMPv3 are supported) +17. IPv4 +18. IPv6 +19. MPLS +20. NDP +21. Raw IP (IPv4 & IPv6) + +### Transport Layer (L4) + +22. GTP (v1) +23. IPSec AH & ESP - parsing only (no editing capabilities) +24. TCP +25. UDP + +### Session Layer (L5) + +26. SDP +27. SIP + +### Presentation Layer (L6) + +28. SSL/TLS - parsing only (no editing capabilities) + +### Application Layer (L7) + +29. BGP (v4) +30. DHCP +31. DHCPv6 +32. DNS +33. FTP +34. HTTP headers (request & response) +35. NTP (v3, v4) +36. Radius +37. SOME/IP +38. SSH - parsing only (no editing capabilities) +39. Telnet - parsing only (no editing capabilities) +40. Generic payload + +## DPDK And PF_RING Support + +[The Data Plane Development Kit (DPDK)](https://www.dpdk.org/) is a set of data plane libraries and network interface controller drivers for fast packet processing. + +[PF_RING™](https://www.ntop.org/products/packet-capture/pf_ring/) is a new type of network socket that dramatically improves the packet capture speed. + +Both frameworks provide very fast packets processing (up to line speed) and are used in many network applications such as routers, firewalls, load balancers, etc. +PcapPlusPLus provides a C++ abstraction layer over DPDK & PF_RING. This abstraction layer provides an easy to use interface that removes a lot of the boilerplate involved in using these frameworks. You can learn more by visiting the [DPDK](https://pcapplusplus.github.io/docs/dpdk) & [PF_RING](https://pcapplusplus.github.io/docs/features#pf_ring-support) support pages in PcapPlusPlus web-site. + +## Benchmarks + +We used Matias Fontanini's [packet-capture-benchmarks](https://github.com/mfontanini/packet-capture-benchmarks) project to compare the performance of PcapPlusPlus with other similar C++ libraries (such as `libtins` and `libcrafter`). + +You can see the results in the [Benchmarks](https://pcapplusplus.github.io/docs/benchmark) page in PcapPlusPlus web-site. + +## Provide Feedback + +We'd be more than happy to get feedback, please feel free to reach out to us in any of the following ways: + +- Open a GitHub ticket +- Post a message in PcapPlusPlus Google group: +- Ask a question on Stack Overflow: +- Send an email to: +- Follow us on Twitter: + +If you like this project please __Star us on GitHub — it helps!__ :star: :star: + +Please visit the [PcapPlusPlus web-site](https://pcapplusplus.github.io/community) to learn more. + +## Contributing + +We would very much appreciate any contribution to this project. If you're interested in contributing please visit the [contribution page](https://pcapplusplus.github.io/community#contribute) in PcapPlusPlus web-site. + +## License + +PcapPlusPlus is released under the [Unlicense license](https://unlicense.org/). + +[![GitHub](https://img.shields.io/github/license/seladb/PcapPlusPlus?style=flat&color=blue&logo=unlicense)](https://unlicense.org/)