diff --git a/Common++/header/Logger.h b/Common++/header/Logger.h index 84638a5115..cc22e355c0 100644 --- a/Common++/header/Logger.h +++ b/Common++/header/Logger.h @@ -102,6 +102,7 @@ namespace pcpp PacketLogModuleSmtpLayer, ///< SmtpLayer module (Packet++) PacketLogModuleWireGuardLayer, ///< WireGuardLayer module (Packet++) PacketLogModuleDoIpLayer, ///< DoipLayer module (Packet++) + PacketLogModuleGeneveLayer, ///< GeneveLayer module (Packet++) PcapLogModuleWinPcapLiveDevice, ///< WinPcapLiveDevice module (Pcap++) PcapLogModuleRemoteDevice, ///< WinPcapRemoteDevice module (Pcap++) PcapLogModuleLiveDevice, ///< PcapLiveDevice module (Pcap++) diff --git a/Packet++/CMakeLists.txt b/Packet++/CMakeLists.txt index 090d2208b7..8d3438c3c9 100644 --- a/Packet++/CMakeLists.txt +++ b/Packet++/CMakeLists.txt @@ -16,6 +16,7 @@ add_library( src/EthDot3Layer.cpp src/EthLayer.cpp src/FtpLayer.cpp + src/GeneveLayer.cpp src/GreLayer.cpp src/GtpLayer.cpp src/HttpLayer.cpp @@ -98,6 +99,7 @@ set( header/EthDot3Layer.h header/EthLayer.h header/FtpLayer.h + header/GeneveLayer.h header/GreLayer.h header/GtpLayer.h header/HttpLayer.h diff --git a/Packet++/header/GeneveLayer.h b/Packet++/header/GeneveLayer.h new file mode 100644 index 0000000000..716fe225ec --- /dev/null +++ b/Packet++/header/GeneveLayer.h @@ -0,0 +1,451 @@ +#pragma once + +#include "Layer.h" +#include "TLVData.h" + +#include +#include + +/// @file + +/// @namespace pcpp +/// @brief The main namespace for the PcapPlusPlus lib +namespace pcpp +{ + class GeneveLayer; + class GeneveOptionIterator; + + /// @struct geneve_header + /// Represents the fixed part of a GENEVE protocol header +#pragma pack(push, 1) + struct geneve_header + { + static constexpr size_t OptionsLengthUnit = 4; + static constexpr size_t MaxOptionsLength = ((1 << 6) - 1) * OptionsLengthUnit; + +#if (BYTE_ORDER == LITTLE_ENDIAN) + /// Options length in 4-byte units + uint8_t optionsLength : 6; + /// Protocol version + uint8_t version : 2; + /// Reserved bits + uint8_t reserved1 : 6; + /// Critical options present flag + uint8_t criticalFlag : 1; + /// Operations, administration, and maintenance packet flag + uint8_t oamFlag : 1; +#else + /// Protocol version + uint8_t version : 2; + /// Options length in 4-byte units + uint8_t optionsLength : 6; + /// Operations, administration, and maintenance packet flag + uint8_t oamFlag : 1; + /// Critical options present flag + uint8_t criticalFlag : 1; + /// Reserved bits + uint8_t reserved1 : 6; +#endif + /// EtherType of the encapsulated protocol + uint16_t protocolType; + /// Virtual network identifier + uint8_t vni[3]; + /// Reserved byte + uint8_t reserved2; + + /// @return Options length in bytes + size_t getOptionsLength() const + { + return static_cast(optionsLength) * OptionsLengthUnit; + } + + /// @param[in] value Options length in bytes + /// @pre value must be divisible by 4 and no greater than MaxOptionsLength + void setOptionsLength(size_t value) + { + optionsLength = static_cast(value / OptionsLengthUnit); + } + + /// @return The 24-bit virtual network identifier + uint32_t getVNI() const + { + return (static_cast(vni[0]) << 16) | (static_cast(vni[1]) << 8) | vni[2]; + } + + /// @param[in] value The 24-bit virtual network identifier + void setVNI(uint32_t value) + { + vni[0] = static_cast((value >> 16) & 0xff); + vni[1] = static_cast((value >> 8) & 0xff); + vni[2] = static_cast(value & 0xff); + } + }; +#pragma pack(pop) + static_assert(sizeof(geneve_header) == 8, "geneve_header size is not 8 bytes"); + + /// @struct geneve_option_header + /// Represents a GENEVE option header +#pragma pack(push, 1) + struct geneve_option_header + { + static constexpr size_t DataLengthUnit = 4; + static constexpr size_t MaxDataLength = ((1 << 5) - 1) * DataLengthUnit; + static constexpr uint8_t TypeMask = 0x7f; + static constexpr uint8_t CriticalBitMask = 0x80; + + /// Option namespace assigned by IANA + uint16_t optionClass; + /// @return Option class in host byte order + uint16_t getOptionClass() const; + + /// @param[in] value Option class in host byte order + void setOptionClass(uint16_t value); + + /// Option type. The most significant bit is the critical bit + uint8_t type; +#if (BYTE_ORDER == LITTLE_ENDIAN) + /// Option data length in 4-byte units + uint8_t length : 5; + /// Reserved bits + uint8_t reserved : 3; +#else + /// Reserved bits + uint8_t reserved : 3; + /// Option data length in 4-byte units + uint8_t length : 5; +#endif + + /// @return The 7-bit option type without the critical bit + uint8_t getType() const + { + return extractType(type); + } + + /// @param[in] value A raw option type, optionally including the critical bit + /// @return The 7-bit option type without the critical bit + static uint8_t extractType(uint8_t value) + { + return static_cast(value & TypeMask); + } + + /// Round an option data length up to the next 4-byte boundary + /// @param[in] value Unpadded option data length in bytes + /// @return Option data length rounded up to a multiple of 4 + static size_t alignDataSize(size_t value) + { + constexpr size_t AlignmentMask = DataLengthUnit - 1; + return (value + AlignmentMask) & ~AlignmentMask; + } + + /// @return True if the critical bit is set + bool isCritical() const + { + return (type & CriticalBitMask) != 0; + } + + /// @return Option data length in bytes + size_t getDataSize() const + { + return static_cast(length) * DataLengthUnit; + } + + /// @param[in] value Option data length in bytes + /// @pre value must be divisible by 4 and no greater than MaxDataLength + void setDataSize(size_t value) + { + length = static_cast(value / DataLengthUnit); + } + + /// @return Total option size including its 4-byte header + size_t getTotalSize() const + { + return sizeof(geneve_option_header) + getDataSize(); + } + + /// @param[in] value The 7-bit option type + /// @param[in] critical Whether to set the critical bit + void setType(uint8_t value, bool critical) + { + type = static_cast(extractType(value) | (critical ? CriticalBitMask : 0)); + } + }; +#pragma pack(pop) + static_assert(sizeof(geneve_option_header) == 4, "geneve_option_header size is not 4 bytes"); + + /// @class GeneveOption + /// A non-owning view of a GENEVE option. The view is invalidated when its underlying data is destroyed or moved, + /// including when options are added to or removed from the containing GeneveLayer + class GeneveOption + { + friend class GeneveLayer; + friend class GeneveOptionIterator; + friend class GeneveOptionRange; + + private: + geneve_option_header* m_Data; + + explicit GeneveOption(geneve_option_header& optionData) : m_Data(&optionData) + {} + + static bool canAssign(const uint8_t* optionRawData, size_t optionDataLen); + + public: + /// @return Option class in host byte order + uint16_t getOptionClass() const; + + /// @return The 7-bit option type without the critical bit + uint8_t getType() const; + + /// @return True if the option is critical + bool isCritical() const; + + /// @return Option data length in bytes + size_t getDataSize() const; + + /// @return Total option size including its 4-byte header + size_t getTotalSize() const; + + /// @return A pointer to the option data + uint8_t* getData() const; + + /// @return A pointer to the option header + uint8_t* getRecordBasePtr() const + { + return reinterpret_cast(m_Data); + } + }; + + /// @class GeneveOptionIterator + /// An input iterator over structurally valid GENEVE options + class GeneveOptionIterator + { + friend class GeneveOptionRange; + + private: + uint8_t* m_Current; + uint8_t* m_End; + + GeneveOptionIterator(uint8_t* current, uint8_t* end) : m_Current(current), m_End(end) + {} + + public: + using iterator_category = std::input_iterator_tag; + using value_type = GeneveOption; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = GeneveOption; + + /// Dereference this iterator + /// @return A valid non-owning option view + /// @pre This iterator must not equal the end iterator + GeneveOption operator*() const; + + /// Advance to the next structurally valid option, or to the end iterator + /// @return This iterator + GeneveOptionIterator& operator++(); + + /// Advance to the next structurally valid option, or to the end iterator + /// @return The iterator value before it was advanced + GeneveOptionIterator operator++(int); + + /// Compare two option iterators + /// @param[in] other The iterator to compare + /// @return True if both iterators refer to the same position in the same range + bool operator==(const GeneveOptionIterator& other) const + { + return m_Current == other.m_Current && m_End == other.m_End; + } + + /// Compare two option iterators + /// @param[in] other The iterator to compare + /// @return True if the iterators refer to different positions or ranges + bool operator!=(const GeneveOptionIterator& other) const + { + return !operator==(other); + } + }; + + /// @class GeneveOptionRange + /// A non-owning range of GENEVE options. The range and all iterators obtained from it are invalidated when the + /// containing GeneveLayer is modified or destroyed + class GeneveOptionRange + { + friend class GeneveLayer; + + private: + uint8_t* m_Begin; + uint8_t* m_End; + + GeneveOptionRange(uint8_t* begin, uint8_t* end); + + public: + /// Construct an empty option range + GeneveOptionRange() : m_Begin(nullptr), m_End(nullptr) + {} + + /// @return An iterator to the first option, or end() if the range is empty + GeneveOptionIterator begin() const + { + return GeneveOptionIterator(m_Begin, m_End); + } + + /// @return The iterator marking the end of this range + GeneveOptionIterator end() const + { + return GeneveOptionIterator(m_End, m_End); + } + + /// Find the first option matching a class and type + /// @param[in] optionClass Option class in host byte order + /// @param[in] optionType The 7-bit option type + /// @return An iterator to the matching option, or end() if no option matches + GeneveOptionIterator find(uint16_t optionClass, uint8_t optionType) const; + + /// @return The number of structurally valid options in this range + /// @note This operation has O(n) time complexity, where n is the number of options. + size_t size() const; + + /// @return True if this range contains no options + bool empty() const + { + return m_Begin == m_End; + } + }; + + /// @class GeneveOptionBuilder + /// Builds GENEVE options. Option data is padded with zeroes to a 4-byte boundary + class GeneveOptionBuilder : public TLVRecordBuilder + { + private: + uint16_t m_OptionClass; + bool m_Critical; + + public: + /// Construct a GENEVE option builder + /// @param[in] optionClass Option namespace assigned by IANA + /// @param[in] optionType The 7-bit option type + /// @param[in] optionData A read-only buffer containing option data + /// @param[in] optionDataLen Option data length in bytes. The maximum supported length is 124 bytes + /// @param[in] critical Set the option critical bit + GeneveOptionBuilder(uint16_t optionClass, uint8_t optionType, const uint8_t* optionData, uint8_t optionDataLen, + bool critical = false) + : TLVRecordBuilder(optionType, optionData, optionDataLen), m_OptionClass(optionClass), m_Critical(critical) + {} + + /// Build a GENEVE option into an owning byte buffer + /// @return The encoded option, or an empty buffer if the data is too long + std::vector build() const; + }; + + /// @class GeneveLayer + /// Represents a GENEVE (Generic Network Virtualization Encapsulation) protocol layer + class GeneveLayer : public Layer + { + public: + /// The IANA-assigned UDP destination port for GENEVE + static constexpr uint16_t DefaultPort = 6081; + + /// Construct a layer from existing packet 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 the layer is stored + /// @note This constructor does not validate the input. Use isDataValid() before constructing a standalone + /// parsed layer. + GeneveLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet, Geneve) + {} + + /// Construct a new GENEVE layer + /// @param[in] vni The 24-bit virtual network identifier + /// @param[in] protocolType EtherType of the encapsulated protocol. Defaults to Transparent Ethernet Bridging + /// @param[in] oamFlag Set the operations, administration, and maintenance flag + explicit GeneveLayer(uint32_t vni = 0, uint16_t protocolType = 0x6558, bool oamFlag = false); + + ~GeneveLayer() override = default; + + /// Validate a GENEVE byte stream, including all declared options + /// @param[in] data The beginning of the GENEVE header + /// @param[in] dataLen Available bytes + /// @return True if the data contains a supported, structurally valid GENEVE header + static bool isDataValid(const uint8_t* data, size_t dataLen); + + /// Check whether a UDP port is the standard GENEVE destination port + /// @param[in] port UDP port in host byte order + /// @return True for port 6081 + static bool isGenevePort(uint16_t port) + { + return port == DefaultPort; + } + + /// @return A pointer to the fixed GENEVE header + /// @pre The layer must contain a complete fixed GENEVE header + geneve_header* getGeneveHeader() const + { + return reinterpret_cast(m_Data); + } + + /// @return The VNI in host byte order + /// @pre The layer must contain a complete fixed GENEVE header + uint32_t getVNI() const; + + /// Set the VNI. Only the least significant 24 bits are used + /// @param[in] vni The VNI to set + /// @pre The layer must contain a complete fixed GENEVE header + void setVNI(uint32_t vni); + + /// @return Encapsulated protocol EtherType in host byte order + /// @pre The layer must contain a complete fixed GENEVE header + uint16_t getProtocolType() const; + + /// Set the encapsulated protocol EtherType + /// @param[in] protocolType EtherType in host byte order + /// @pre The layer must contain a complete fixed GENEVE header + void setProtocolType(uint16_t protocolType); + + /// @return Total options length in bytes, or zero if the fixed GENEVE header is unavailable or truncated + size_t getOptionsLength() const; + + /// @return Number of structurally valid options in this layer, or zero if no valid option range is available + size_t getOptionCount() const; + + /// @return A non-owning range over the options in this layer, or an empty range if the fixed GENEVE header is + /// unavailable or truncated + GeneveOptionRange getOptions() const; + + /// Add an option after all existing options + /// @param[in] optionBuilder Builder containing the option to add + /// @return True if the option was added successfully + bool addOption(const GeneveOptionBuilder& optionBuilder); + + /// Remove the first option matching a class and type + /// @param[in] optionClass Option class in host byte order + /// @param[in] optionType The 7-bit option type + /// @return True if an option was found and removed + bool removeOption(uint16_t optionClass, uint8_t optionType); + + /// Remove all options + /// @return True if all options were removed + bool removeAllOptions(); + + /// Parse the encapsulated protocol according to the Protocol Type field + void parseNextLayer() override; + + /// @return Zero if no data is available; the available data length if the fixed header is truncated; otherwise, + /// the fixed header plus the declared options length, capped at the available data length + size_t getHeaderLen() const override; + + /// Update the Protocol Type and Critical flag from the following layer and options + void computeCalculateFields() override; + + std::string toString() const override; + + OsiModelLayer getOsiModelLayer() const override + { + return OsiModelDataLinkLayer; + } + + private: + void updateCriticalFlag(); + }; +} // namespace pcpp diff --git a/Packet++/header/ProtocolType.h b/Packet++/header/ProtocolType.h index 0de4ffaf55..18d9315775 100644 --- a/Packet++/header/ProtocolType.h +++ b/Packet++/header/ProtocolType.h @@ -260,6 +260,9 @@ namespace pcpp /// MySQL protocol const ProtocolType MySQL = 63; + /// GENEVE protocol + const ProtocolType Geneve = 65; + /// FTP protocol family (FTPControl and FtpData protocols) const ProtocolTypeFamily FTP = 0x3c29; diff --git a/Packet++/header/UdpLayer.h b/Packet++/header/UdpLayer.h index c78bc2ded9..1963777156 100644 --- a/Packet++/header/UdpLayer.h +++ b/Packet++/header/UdpLayer.h @@ -72,8 +72,8 @@ namespace pcpp // implement abstract methods - /// Currently identifies the following next layers: DnsLayer, DhcpLayer, VxlanLayer, SipRequestLayer, - /// SipResponseLayer, RadiusLayer. Otherwise sets PayloadLayer + /// Currently identifies the following next layers: DnsLayer, DhcpLayer, VxlanLayer, GeneveLayer, + /// SipRequestLayer, SipResponseLayer, RadiusLayer. Otherwise sets PayloadLayer void parseNextLayer() override; /// @return Size of @ref udphdr diff --git a/Packet++/src/GeneveLayer.cpp b/Packet++/src/GeneveLayer.cpp new file mode 100644 index 0000000000..8492464055 --- /dev/null +++ b/Packet++/src/GeneveLayer.cpp @@ -0,0 +1,402 @@ +#define LOG_MODULE PacketLogModuleGeneveLayer + +#include "GeneveLayer.h" +#include "ArpLayer.h" +#include "EndianPortable.h" +#include "EthDot3Layer.h" +#include "EthLayer.h" +#include "IPv4Layer.h" +#include "IPv6Layer.h" +#include "Logger.h" +#include "MplsLayer.h" +#include "PayloadLayer.h" +#include "VlanLayer.h" + +#include +#include +#include + +namespace pcpp +{ + uint16_t geneve_option_header::getOptionClass() const + { + return be16toh(optionClass); + } + + void geneve_option_header::setOptionClass(uint16_t value) + { + optionClass = htobe16(value); + } + + bool GeneveOption::canAssign(const uint8_t* optionRawData, size_t optionDataLen) + { + if (optionRawData == nullptr || optionDataLen < sizeof(geneve_option_header)) + return false; + + return reinterpret_cast(optionRawData)->getTotalSize() <= optionDataLen; + } + + uint16_t GeneveOption::getOptionClass() const + { + return m_Data->getOptionClass(); + } + + uint8_t GeneveOption::getType() const + { + return m_Data->getType(); + } + + bool GeneveOption::isCritical() const + { + return m_Data->isCritical(); + } + + size_t GeneveOption::getDataSize() const + { + return m_Data->getDataSize(); + } + + size_t GeneveOption::getTotalSize() const + { + return m_Data->getTotalSize(); + } + + uint8_t* GeneveOption::getData() const + { + return reinterpret_cast(m_Data) + sizeof(geneve_option_header); + } + + GeneveOption GeneveOptionIterator::operator*() const + { + return GeneveOption(*reinterpret_cast(m_Current)); + } + + GeneveOptionIterator& GeneveOptionIterator::operator++() + { + if (m_Current == m_End) + return *this; + + size_t remaining = static_cast(m_End - m_Current); + if (!GeneveOption::canAssign(m_Current, remaining)) + { + m_Current = m_End; + return *this; + } + + m_Current += (**this).getTotalSize(); + if (m_Current != m_End && !GeneveOption::canAssign(m_Current, static_cast(m_End - m_Current))) + { + m_Current = m_End; + } + + return *this; + } + + GeneveOptionIterator GeneveOptionIterator::operator++(int) + { + GeneveOptionIterator previous = *this; + ++(*this); + return previous; + } + + GeneveOptionRange::GeneveOptionRange(uint8_t* begin, uint8_t* end) : m_Begin(begin), m_End(end) + { + if (m_Begin == nullptr || m_Begin == m_End || + !GeneveOption::canAssign(m_Begin, static_cast(m_End - m_Begin))) + { + m_Begin = m_End; + } + } + + GeneveOptionIterator GeneveOptionRange::find(uint16_t optionClass, uint8_t optionType) const + { + for (auto iterator = begin(); iterator != end(); ++iterator) + { + GeneveOption option = *iterator; + if (option.getOptionClass() == optionClass && + option.getType() == geneve_option_header::extractType(optionType)) + return iterator; + } + + return end(); + } + + size_t GeneveOptionRange::size() const + { + size_t count = 0; + for (auto iterator = begin(); iterator != end(); ++iterator) + ++count; + return count; + } + + std::vector GeneveOptionBuilder::build() const + { + if (m_RecValueLen > geneve_option_header::MaxDataLength) + return {}; + + size_t paddedDataLength = geneve_option_header::alignDataSize(m_RecValueLen); + + size_t totalLength = sizeof(geneve_option_header) + paddedDataLength; + std::vector optionData(totalLength, 0); + + geneve_option_header header = {}; + header.setOptionClass(m_OptionClass); + header.setType(m_RecType, m_Critical); + header.setDataSize(paddedDataLength); + memcpy(optionData.data(), &header, sizeof(header)); + if (m_RecValueLen > 0) + memcpy(optionData.data() + sizeof(geneve_option_header), m_RecValue, m_RecValueLen); + + return optionData; + } + + GeneveLayer::GeneveLayer(uint32_t vni, uint16_t protocolType, bool oamFlag) + { + allocData(sizeof(geneve_header)); + m_Protocol = Geneve; + setVNI(vni); + setProtocolType(protocolType); + getGeneveHeader()->oamFlag = oamFlag ? 1 : 0; + } + + bool GeneveLayer::isDataValid(const uint8_t* data, size_t dataLen) + { + if (!canReinterpretAs(data, dataLen)) + return false; + + auto* header = reinterpret_cast(data); + // RFC 8926 defines GENEVE version 0; this implementation supports that version only. + if (header->version != 0) + return false; + // RFC 8926 Section 3.4 requires Protocol Type to follow the EtherType convention, + // whose valid encodings start at 0x0600. + if (be16toh(header->protocolType) < 0x0600) + return false; + + auto optionsLength = header->getOptionsLength(); + if (optionsLength > dataLen - sizeof(geneve_header)) + return false; + + const uint8_t* option = data + sizeof(geneve_header); + size_t remaining = optionsLength; + while (remaining > 0) + { + if (!GeneveOption::canAssign(option, remaining)) + return false; + if (reinterpret_cast(option)->isCritical() && header->criticalFlag == 0) + return false; + + size_t optionLength = reinterpret_cast(option)->getTotalSize(); + option += optionLength; + remaining -= optionLength; + } + + return true; + } + + uint32_t GeneveLayer::getVNI() const + { + return getGeneveHeader()->getVNI(); + } + + void GeneveLayer::setVNI(uint32_t vni) + { + getGeneveHeader()->setVNI(vni); + } + + uint16_t GeneveLayer::getProtocolType() const + { + return be16toh(getGeneveHeader()->protocolType); + } + + void GeneveLayer::setProtocolType(uint16_t protocolType) + { + getGeneveHeader()->protocolType = htobe16(protocolType); + } + + size_t GeneveLayer::getOptionsLength() const + { + if (m_Data == nullptr || m_DataLen < sizeof(geneve_header)) + return 0; + return getGeneveHeader()->getOptionsLength(); + } + + size_t GeneveLayer::getHeaderLen() const + { + if (m_Data == nullptr) + return 0; + if (m_DataLen < sizeof(geneve_header)) + return m_DataLen; + + return (std::min)(m_DataLen, sizeof(geneve_header) + getOptionsLength()); + } + + GeneveOptionRange GeneveLayer::getOptions() const + { + if (m_Data == nullptr || m_DataLen <= sizeof(geneve_header)) + return {}; + + size_t optionsLength = getHeaderLen() - sizeof(geneve_header); + uint8_t* options = m_Data + sizeof(geneve_header); + return GeneveOptionRange(options, options + optionsLength); + } + + size_t GeneveLayer::getOptionCount() const + { + return getOptions().size(); + } + + bool GeneveLayer::addOption(const GeneveOptionBuilder& optionBuilder) + { + std::vector optionData = optionBuilder.build(); + if (optionData.empty()) + { + PCPP_LOG_ERROR("Cannot build GENEVE option"); + return false; + } + + size_t oldOptionsLength = getOptionsLength(); + if (oldOptionsLength + optionData.size() > geneve_header::MaxOptionsLength) + { + PCPP_LOG_ERROR("GENEVE options exceed the maximum length of 252 bytes"); + return false; + } + + int offset = static_cast(sizeof(geneve_header) + oldOptionsLength); + size_t optionSize = optionData.size(); + if (!extendLayer(offset, optionSize)) + { + PCPP_LOG_ERROR("Could not extend GeneveLayer by " << optionSize << " bytes"); + return false; + } + + memcpy(m_Data + offset, optionData.data(), optionSize); + getGeneveHeader()->setOptionsLength(oldOptionsLength + optionSize); + updateCriticalFlag(); + return true; + } + + bool GeneveLayer::removeOption(uint16_t optionClass, uint8_t optionType) + { + GeneveOptionRange options = getOptions(); + GeneveOptionIterator optionIterator = options.find(optionClass, optionType); + if (optionIterator == options.end()) + return false; + GeneveOption option = *optionIterator; + + size_t oldOptionsLength = getOptionsLength(); + size_t optionSize = option.getTotalSize(); + int offset = static_cast(option.getRecordBasePtr() - m_Data); + if (!shortenLayer(offset, optionSize)) + return false; + + getGeneveHeader()->setOptionsLength(oldOptionsLength - optionSize); + updateCriticalFlag(); + return true; + } + + bool GeneveLayer::removeAllOptions() + { + size_t optionsLength = getOptionsLength(); + if (optionsLength == 0) + { + getGeneveHeader()->criticalFlag = 0; + return true; + } + + if (!shortenLayer(sizeof(geneve_header), optionsLength)) + return false; + + getGeneveHeader()->optionsLength = 0; + getGeneveHeader()->criticalFlag = 0; + return true; + } + + void GeneveLayer::updateCriticalFlag() + { + getGeneveHeader()->criticalFlag = 0; + for (GeneveOption option : getOptions()) + { + if (option.isCritical()) + { + getGeneveHeader()->criticalFlag = 1; + return; + } + } + } + + void GeneveLayer::parseNextLayer() + { + size_t headerLength = getHeaderLen(); + if (m_DataLen <= headerLength) + return; + + uint8_t* payload = m_Data + headerLength; + size_t payloadLength = m_DataLen - headerLength; + switch (getProtocolType()) + { + case PCPP_ETHERTYPE_IP: + tryConstructNextLayerWithFallback(payload, payloadLength); + break; + case PCPP_ETHERTYPE_ARP: + tryConstructNextLayerWithFallback(payload, payloadLength); + break; + case PCPP_ETHERTYPE_IPV6: + tryConstructNextLayerWithFallback(payload, payloadLength); + break; + case PCPP_ETHERTYPE_VLAN: + case PCPP_ETHERTYPE_IEEE_802_1AD: + tryConstructNextLayerWithFallback(payload, payloadLength); + break; + case PCPP_ETHERTYPE_MPLS: + tryConstructNextLayerWithFallback(payload, payloadLength); + break; + case PCPP_ETHERTYPE_ETHBRIDGE: + if (tryConstructNextLayer(payload, payloadLength) == nullptr) + tryConstructNextLayerWithFallback(payload, payloadLength); + break; + default: + constructNextLayer(payload, payloadLength); + break; + } + } + + void GeneveLayer::computeCalculateFields() + { + updateCriticalFlag(); + if (m_NextLayer == nullptr) + return; + + switch (m_NextLayer->getProtocol()) + { + case IPv4: + setProtocolType(PCPP_ETHERTYPE_IP); + break; + case ARP: + setProtocolType(PCPP_ETHERTYPE_ARP); + break; + case IPv6: + setProtocolType(PCPP_ETHERTYPE_IPV6); + break; + case VLAN: + setProtocolType(PCPP_ETHERTYPE_VLAN); + break; + case MPLS: + setProtocolType(PCPP_ETHERTYPE_MPLS); + break; + case Ethernet: + case EthernetDot3: + setProtocolType(PCPP_ETHERTYPE_ETHBRIDGE); + break; + default: + break; + } + } + + std::string GeneveLayer::toString() const + { + std::ostringstream result; + result << "GENEVE Layer, VNI: " << getVNI() << ", Protocol type: 0x" << std::hex << getProtocolType(); + return result.str(); + } +} // namespace pcpp diff --git a/Packet++/src/UdpLayer.cpp b/Packet++/src/UdpLayer.cpp index 9a7bc03d55..a9481ad3f3 100644 --- a/Packet++/src/UdpLayer.cpp +++ b/Packet++/src/UdpLayer.cpp @@ -10,6 +10,7 @@ #include "DhcpV6Layer.h" #include "DoIpLayer.h" #include "VxlanLayer.h" +#include "GeneveLayer.h" #include "SipLayer.h" #include "RadiusLayer.h" #include "GtpLayer.h" @@ -110,6 +111,10 @@ namespace pcpp { tryConstructNextLayerWithFallback(udpData, udpDataLen); } + else if (GeneveLayer::isGenevePort(portDst)) + { + tryConstructNextLayerWithFallback(udpData, udpDataLen); + } else if (DnsLayer::isDataValid(udpData, udpDataLen) && (DnsLayer::isDnsPort(portDst) || DnsLayer::isDnsPort(portSrc))) { diff --git a/Tests/Packet++Test/CMakeLists.txt b/Tests/Packet++Test/CMakeLists.txt index 65155c3dca..6ab46ed1a3 100644 --- a/Tests/Packet++Test/CMakeLists.txt +++ b/Tests/Packet++Test/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable( Tests/DoIpTests.cpp Tests/EthAndArpTests.cpp Tests/FtpTests.cpp + Tests/GeneveTests.cpp Tests/GreTests.cpp Tests/GtpTests.cpp Tests/HttpTests.cpp diff --git a/Tests/Packet++Test/TestDefinition.h b/Tests/Packet++Test/TestDefinition.h index 6a3ae32b73..ffd0d8bbc6 100644 --- a/Tests/Packet++Test/TestDefinition.h +++ b/Tests/Packet++Test/TestDefinition.h @@ -17,6 +17,10 @@ PTF_TEST_CASE(QinQ802_1adParse); PTF_TEST_CASE(MplsLayerTest); PTF_TEST_CASE(VxlanParsingAndCreationTest); +// Implemented in GeneveTests.cpp +PTF_TEST_CASE(GeneveParsingAndCreationTest); +PTF_TEST_CASE(GeneveMalformedPacketTest); + // Implemented in IPv4Tests.cpp PTF_TEST_CASE(IPv4PacketCreation); PTF_TEST_CASE(IPv4PacketParsing); diff --git a/Tests/Packet++Test/Tests/GeneveTests.cpp b/Tests/Packet++Test/Tests/GeneveTests.cpp new file mode 100644 index 0000000000..dd1bbdbb0a --- /dev/null +++ b/Tests/Packet++Test/Tests/GeneveTests.cpp @@ -0,0 +1,209 @@ +#include "../TestDefinition.h" +#include "ArpLayer.h" +#include "EthLayer.h" +#include "GeneveLayer.h" +#include "IPv4Layer.h" +#include "Packet.h" +#include "PayloadLayer.h" +#include "RawPacket.h" +#include "UdpLayer.h" + +#include + +PTF_TEST_CASE(GeneveParsingAndCreationTest) +{ + pcpp::geneve_option_header optionHeader = {}; + optionHeader.setOptionClass(0x1234); + PTF_ASSERT_EQUAL(optionHeader.getOptionClass(), 0x1234); + + pcpp::GeneveLayer geneveLayer(0xabcdef, PCPP_ETHERTYPE_ETHBRIDGE, true); + const uint8_t optionData[] = { 1, 2, 3, 4, 5 }; + PTF_ASSERT_TRUE(geneveLayer.addOption(pcpp::GeneveOptionBuilder(0x0102, 3, optionData, sizeof(optionData)))); + pcpp::GeneveOptionRange firstOptions = geneveLayer.getOptions(); + pcpp::GeneveOptionIterator firstOptionIterator = firstOptions.find(0x0102, 3); + PTF_ASSERT_TRUE(firstOptionIterator != firstOptions.end()); + pcpp::GeneveOption firstOption = *firstOptionIterator; + PTF_ASSERT_EQUAL(firstOption.getOptionClass(), 0x0102); + PTF_ASSERT_EQUAL(firstOption.getType(), 3); + PTF_ASSERT_FALSE(firstOption.isCritical()); + PTF_ASSERT_EQUAL(firstOption.getDataSize(), 8); + PTF_ASSERT_BUF_COMPARE(firstOption.getData(), optionData, sizeof(optionData)); + PTF_ASSERT_EQUAL(firstOption.getData()[5], 0); + PTF_ASSERT_EQUAL(firstOption.getData()[6], 0); + PTF_ASSERT_EQUAL(firstOption.getData()[7], 0); + + PTF_ASSERT_TRUE(geneveLayer.addOption(pcpp::GeneveOptionBuilder(0x0102, 4, nullptr, 0, true))); + pcpp::GeneveOptionRange options = geneveLayer.getOptions(); + pcpp::GeneveOptionIterator secondOptionIterator = options.find(0x0102, 4); + PTF_ASSERT_TRUE(secondOptionIterator != options.end()); + pcpp::GeneveOption secondOption = *secondOptionIterator; + PTF_ASSERT_TRUE(secondOption.isCritical()); + PTF_ASSERT_EQUAL(geneveLayer.getOptionsLength(), 16); + PTF_ASSERT_EQUAL(geneveLayer.getHeaderLen(), 24); + PTF_ASSERT_EQUAL(geneveLayer.getOptionCount(), 2); + PTF_ASSERT_EQUAL(geneveLayer.getGeneveHeader()->criticalFlag, 1); + + pcpp::EthLayer outerEth(pcpp::MacAddress("00:11:22:33:44:55"), pcpp::MacAddress("66:77:88:99:aa:bb")); + pcpp::IPv4Layer outerIp(pcpp::IPv4Address("192.0.2.1"), pcpp::IPv4Address("192.0.2.2")); + pcpp::UdpLayer outerUdp(12345, pcpp::GeneveLayer::DefaultPort); + pcpp::EthLayer innerEth(pcpp::MacAddress("10:11:12:13:14:15"), pcpp::MacAddress("20:21:22:23:24:25")); + pcpp::IPv4Layer innerIp(pcpp::IPv4Address("198.51.100.1"), pcpp::IPv4Address("198.51.100.2")); + const uint8_t payloadData[] = { 0xde, 0xad, 0xbe, 0xef }; + pcpp::PayloadLayer payload(payloadData, sizeof(payloadData)); + + pcpp::Packet craftedPacket(128); + PTF_ASSERT_TRUE(craftedPacket.addLayer(&outerEth)); + PTF_ASSERT_TRUE(craftedPacket.addLayer(&outerIp)); + PTF_ASSERT_TRUE(craftedPacket.addLayer(&outerUdp)); + PTF_ASSERT_TRUE(craftedPacket.addLayer(&geneveLayer)); + PTF_ASSERT_TRUE(craftedPacket.addLayer(&innerEth)); + PTF_ASSERT_TRUE(craftedPacket.addLayer(&innerIp)); + PTF_ASSERT_TRUE(craftedPacket.addLayer(&payload)); + craftedPacket.computeCalculateFields(); + + pcpp::RawPacket rawPacket(*craftedPacket.getRawPacket()); + pcpp::Packet parsedPacket(&rawPacket); + pcpp::GeneveLayer* parsedGeneve = parsedPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(parsedGeneve); + PTF_ASSERT_TRUE(parsedPacket.isPacketOfType(pcpp::Geneve)); + PTF_ASSERT_EQUAL(parsedGeneve->getVNI(), 0xabcdef); + PTF_ASSERT_EQUAL(parsedGeneve->getProtocolType(), PCPP_ETHERTYPE_ETHBRIDGE); + PTF_ASSERT_EQUAL(parsedGeneve->getGeneveHeader()->oamFlag, 1); + PTF_ASSERT_EQUAL(parsedGeneve->getGeneveHeader()->criticalFlag, 1); + PTF_ASSERT_EQUAL(parsedGeneve->getOptionCount(), 2); + PTF_ASSERT_EQUAL(parsedGeneve->getHeaderLen(), 24); + + pcpp::GeneveOptionRange parsedOptions = parsedGeneve->getOptions(); + pcpp::GeneveOptionIterator parsedFirstOptionIterator = parsedOptions.begin(); + PTF_ASSERT_TRUE(parsedFirstOptionIterator != parsedOptions.end()); + pcpp::GeneveOption parsedFirstOption = *parsedFirstOptionIterator; + PTF_ASSERT_EQUAL(parsedFirstOption.getOptionClass(), 0x0102); + PTF_ASSERT_EQUAL(parsedFirstOption.getType(), 3); + pcpp::GeneveOptionIterator parsedSecondOptionIterator = parsedFirstOptionIterator; + ++parsedSecondOptionIterator; + PTF_ASSERT_TRUE(parsedSecondOptionIterator != parsedOptions.end()); + pcpp::GeneveOption parsedSecondOption = *parsedSecondOptionIterator; + PTF_ASSERT_TRUE(parsedSecondOption.isCritical()); + ++parsedSecondOptionIterator; + PTF_ASSERT_TRUE(parsedSecondOptionIterator == parsedOptions.end()); + PTF_ASSERT_EQUAL(parsedGeneve->getNextLayer()->getProtocol(), pcpp::Ethernet, enum); + PTF_ASSERT_EQUAL(parsedGeneve->getNextLayer()->getNextLayer()->getProtocol(), pcpp::IPv4, enum); + + size_t packetLength = parsedPacket.getRawPacket()->getRawDataLen(); + PTF_ASSERT_TRUE(parsedGeneve->removeOption(0x0102, 4)); + PTF_ASSERT_EQUAL(parsedPacket.getRawPacket()->getRawDataLen(), packetLength - 4); + PTF_ASSERT_EQUAL(parsedGeneve->getOptionCount(), 1); + PTF_ASSERT_EQUAL(parsedGeneve->getGeneveHeader()->criticalFlag, 0); + PTF_ASSERT_TRUE(parsedGeneve->removeAllOptions()); + PTF_ASSERT_EQUAL(parsedGeneve->getOptionsLength(), 0); + PTF_ASSERT_EQUAL(parsedGeneve->getHeaderLen(), sizeof(pcpp::geneve_header)); + + pcpp::EthLayer outerEth2(pcpp::MacAddress("00:11:22:33:44:55"), pcpp::MacAddress("66:77:88:99:aa:bb")); + pcpp::IPv4Layer outerIp2(pcpp::IPv4Address("192.0.2.1"), pcpp::IPv4Address("192.0.2.2")); + pcpp::UdpLayer outerUdp2(12346, pcpp::GeneveLayer::DefaultPort); + pcpp::GeneveLayer ipGeneveLayer; + pcpp::IPv4Layer directInnerIp(pcpp::IPv4Address("203.0.113.1"), pcpp::IPv4Address("203.0.113.2")); + pcpp::Packet directIpPacket(96); + PTF_ASSERT_TRUE(directIpPacket.addLayer(&outerEth2)); + PTF_ASSERT_TRUE(directIpPacket.addLayer(&outerIp2)); + PTF_ASSERT_TRUE(directIpPacket.addLayer(&outerUdp2)); + PTF_ASSERT_TRUE(directIpPacket.addLayer(&ipGeneveLayer)); + PTF_ASSERT_TRUE(directIpPacket.addLayer(&directInnerIp)); + directIpPacket.computeCalculateFields(); + PTF_ASSERT_EQUAL(ipGeneveLayer.getProtocolType(), PCPP_ETHERTYPE_IP); + + pcpp::RawPacket directIpRawPacket(*directIpPacket.getRawPacket()); + pcpp::Packet parsedDirectIpPacket(&directIpRawPacket); + pcpp::GeneveLayer* parsedIpGeneve = parsedDirectIpPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(parsedIpGeneve); + PTF_ASSERT_NOT_NULL(parsedIpGeneve->getNextLayer()); + PTF_ASSERT_EQUAL(parsedIpGeneve->getNextLayer()->getProtocol(), pcpp::IPv4, enum); + + pcpp::EthLayer outerEth3(pcpp::MacAddress("00:11:22:33:44:55"), pcpp::MacAddress("66:77:88:99:aa:bb")); + pcpp::IPv4Layer outerIp3(pcpp::IPv4Address("192.0.2.1"), pcpp::IPv4Address("192.0.2.2")); + pcpp::UdpLayer outerUdp3(12347, pcpp::GeneveLayer::DefaultPort); + pcpp::GeneveLayer arpGeneveLayer; + pcpp::ArpLayer directInnerArp(pcpp::ArpRequest( + pcpp::MacAddress("10:11:12:13:14:15"), pcpp::IPv4Address("198.51.100.1"), pcpp::IPv4Address("198.51.100.2"))); + pcpp::Packet directArpPacket(128); + PTF_ASSERT_TRUE(directArpPacket.addLayer(&outerEth3)); + PTF_ASSERT_TRUE(directArpPacket.addLayer(&outerIp3)); + PTF_ASSERT_TRUE(directArpPacket.addLayer(&outerUdp3)); + PTF_ASSERT_TRUE(directArpPacket.addLayer(&arpGeneveLayer)); + PTF_ASSERT_TRUE(directArpPacket.addLayer(&directInnerArp)); + directArpPacket.computeCalculateFields(); + PTF_ASSERT_EQUAL(arpGeneveLayer.getProtocolType(), PCPP_ETHERTYPE_ARP); + + pcpp::RawPacket directArpRawPacket(*directArpPacket.getRawPacket()); + pcpp::Packet parsedDirectArpPacket(&directArpRawPacket); + pcpp::GeneveLayer* parsedArpGeneve = parsedDirectArpPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(parsedArpGeneve); + PTF_ASSERT_NOT_NULL(parsedArpGeneve->getNextLayer()); + PTF_ASSERT_EQUAL(parsedArpGeneve->getNextLayer()->getProtocol(), pcpp::ARP, enum); +} // GeneveParsingAndCreationTest + +PTF_TEST_CASE(GeneveMalformedPacketTest) +{ + // Raw headers below use the on-wire layout: Ver/Opt Len, O/C/Reserved, Protocol Type, VNI, Reserved, + // followed by options encoded as Option Class, Type/Critical, and Length. + // A GENEVE base header is 8 bytes, so this buffer is one byte short. + const uint8_t truncatedHeader[7] = {}; + PTF_ASSERT_FALSE(pcpp::GeneveLayer::isDataValid(truncatedHeader, sizeof(truncatedHeader))); + + // Ver=1 is unsupported; Protocol Type=0x6558 (Transparent Ethernet Bridging) and VNI=1 are valid. + uint8_t unsupportedVersion[8] = { 0x40, 0, 0x65, 0x58, 0, 0, 1, 0 }; + PTF_ASSERT_FALSE(pcpp::GeneveLayer::isDataValid(unsupportedVersion, sizeof(unsupportedVersion))); + + // Opt Len=1 declares a 4-byte options area, but the option's Length=1 requires 4 more data bytes. + uint8_t truncatedOptions[12] = { 1, 0, 0x65, 0x58, 0, 0, 1, 0, 0x01, 0x02, 0x03, 1 }; + PTF_ASSERT_FALSE(pcpp::GeneveLayer::isDataValid(truncatedOptions, sizeof(truncatedOptions))); + + // Opt Len=1 contains one complete option with Class=0x0102, Type=3, and no option data. + uint8_t validZeroLengthOption[12] = { 1, 0, 0x65, 0x58, 0, 0, 1, 0, 0x01, 0x02, 0x03, 0 }; + PTF_ASSERT_TRUE(pcpp::GeneveLayer::isDataValid(validZeroLengthOption, sizeof(validZeroLengthOption))); + + // 0x05ff is immediately below the EtherType range and is therefore not a valid Protocol Type. + uint8_t invalidProtocolType[8] = { 0, 0, 0x05, 0xff, 0, 0, 1, 0 }; + PTF_ASSERT_FALSE(pcpp::GeneveLayer::isDataValid(invalidProtocolType, sizeof(invalidProtocolType))); + // 0x0600 is the lower boundary of the EtherType range accepted for Protocol Type. + uint8_t minimumProtocolType[8] = { 0, 0, 0x06, 0, 0, 0, 1, 0 }; + PTF_ASSERT_TRUE(pcpp::GeneveLayer::isDataValid(minimumProtocolType, sizeof(minimumProtocolType))); + + // Option Type=0x83 sets the critical bit for Type=3, but the base header C bit remains clear. + uint8_t criticalOptionWithoutFlag[12] = { 1, 0, 0x65, 0x58, 0, 0, 1, 0, 0x01, 0x02, 0x83, 0 }; + PTF_ASSERT_FALSE(pcpp::GeneveLayer::isDataValid(criticalOptionWithoutFlag, sizeof(criticalOptionWithoutFlag))); + // The same critical option is valid when 0x40 sets the base header C bit. + uint8_t criticalOptionWithFlag[12] = { 1, 0x40, 0x65, 0x58, 0, 0, 1, 0, 0x01, 0x02, 0x83, 0 }; + PTF_ASSERT_TRUE(pcpp::GeneveLayer::isDataValid(criticalOptionWithFlag, sizeof(criticalOptionWithFlag))); + // C=1 without a critical option is accepted because RFC 8926 only mandates the reverse implication. + uint8_t flagWithoutCriticalOption[12] = { 1, 0x40, 0x65, 0x58, 0, 0, 1, 0, 0x01, 0x02, 0x03, 0 }; + PTF_ASSERT_TRUE(pcpp::GeneveLayer::isDataValid(flagWithoutCriticalOption, sizeof(flagWithoutCriticalOption))); + + pcpp::EthLayer outerEth(pcpp::MacAddress("00:11:22:33:44:55"), pcpp::MacAddress("66:77:88:99:aa:bb")); + pcpp::IPv4Layer outerIp(pcpp::IPv4Address("192.0.2.1"), pcpp::IPv4Address("192.0.2.2")); + pcpp::UdpLayer outerUdp(12345, pcpp::GeneveLayer::DefaultPort); + pcpp::GeneveLayer geneveLayer(1); + pcpp::EthLayer innerEth(pcpp::MacAddress("10:11:12:13:14:15"), pcpp::MacAddress("20:21:22:23:24:25")); + + pcpp::Packet craftedPacket(96); + PTF_ASSERT_TRUE(craftedPacket.addLayer(&outerEth)); + PTF_ASSERT_TRUE(craftedPacket.addLayer(&outerIp)); + PTF_ASSERT_TRUE(craftedPacket.addLayer(&outerUdp)); + PTF_ASSERT_TRUE(craftedPacket.addLayer(&geneveLayer)); + PTF_ASSERT_TRUE(craftedPacket.addLayer(&innerEth)); + craftedPacket.computeCalculateFields(); + + const uint8_t* rawData = craftedPacket.getRawPacket()->getRawData(); + std::vector malformedData(rawData, rawData + craftedPacket.getRawPacket()->getRawDataLen()); + constexpr size_t GeneveOffset = sizeof(pcpp::ether_header) + sizeof(pcpp::iphdr) + sizeof(pcpp::udphdr); + // Set Ver=1 in the serialized GENEVE header to verify that UDP parsing falls back to a generic payload. + malformedData[GeneveOffset] = 0x40; + timeval timestamp = {}; + pcpp::RawPacket malformedRawPacket(malformedData.data(), static_cast(malformedData.size()), timestamp, false); + pcpp::Packet malformedPacket(&malformedRawPacket); + PTF_ASSERT_NULL(malformedPacket.getLayerOfType()); + pcpp::UdpLayer* parsedUdp = malformedPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(parsedUdp); + PTF_ASSERT_NOT_NULL(parsedUdp->getNextLayer()); + PTF_ASSERT_EQUAL(parsedUdp->getNextLayer()->getProtocol(), pcpp::GenericPayload, enum); +} // GeneveMalformedPacketTest diff --git a/Tests/Packet++Test/main.cpp b/Tests/Packet++Test/main.cpp index 20180b0426..ed60d4431a 100644 --- a/Tests/Packet++Test/main.cpp +++ b/Tests/Packet++Test/main.cpp @@ -130,6 +130,8 @@ int main(int argc, char* argv[]) PTF_RUN_TEST(QinQ802_1adParse, "vlan"); PTF_RUN_TEST(MplsLayerTest, "mpls"); PTF_RUN_TEST(VxlanParsingAndCreationTest, "vxlan"); + PTF_RUN_TEST(GeneveParsingAndCreationTest, "geneve"); + PTF_RUN_TEST(GeneveMalformedPacketTest, "geneve"); PTF_RUN_TEST(IPv4PacketCreation, "ipv4"); PTF_RUN_TEST(IPv4PacketParsing, "ipv4");