diff --git a/Common++/header/Logger.h b/Common++/header/Logger.h index fafe6a9e37..a186f89563 100644 --- a/Common++/header/Logger.h +++ b/Common++/header/Logger.h @@ -87,6 +87,7 @@ namespace pcpp PacketLogModuleSdpLayer, ///< SdpLayer module (Packet++) PacketLogModuleRadiusLayer, ///< RadiusLayer module (Packet++) PacketLogModuleGtpLayer, ///< GtpLayer module (Packet++) + PacketLogModuleGvcpLayer, ///< GvcpLayer module (Packet++) PacketLogModuleBgpLayer, ///< GtpLayer module (Packet++) PacketLogModuleSSHLayer, ///< SSHLayer module (Packet++) PacketLogModuleVrrpLayer, ///< Vrrp Record module (Packet++) diff --git a/Packet++/CMakeLists.txt b/Packet++/CMakeLists.txt index bab9488be6..e02cee0c12 100644 --- a/Packet++/CMakeLists.txt +++ b/Packet++/CMakeLists.txt @@ -15,6 +15,7 @@ add_library( src/FtpLayer.cpp src/GreLayer.cpp src/GtpLayer.cpp + src/GvcpLayer.cpp src/HttpLayer.cpp src/IcmpLayer.cpp src/IcmpV6Layer.cpp @@ -84,6 +85,7 @@ set(public_headers header/FtpLayer.h header/GreLayer.h header/GtpLayer.h + header/GvcpLayer.h header/HttpLayer.h header/IcmpLayer.h header/IcmpV6Layer.h diff --git a/Packet++/header/GvcpLayer.h b/Packet++/header/GvcpLayer.h new file mode 100644 index 0000000000..67da0f4fa2 --- /dev/null +++ b/Packet++/header/GvcpLayer.h @@ -0,0 +1,755 @@ +#pragma once + +#include "IpAddress.h" +#include "Layer.h" +#include "MacAddress.h" +#include + +/// @file + +/// This GVCP implementation is based on GigE Vision ® Specification version 2.0 + +/** + * @namespace pcpp + * The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + namespace internal + { + static constexpr size_t kGvcpMagicNumber = 0x42; + static constexpr size_t kGvcpRequestHeaderLength = 8; + static constexpr size_t kGvcpAckHeaderLength = 8; + static constexpr size_t kGvcpDiscoveryBodyLength = 248; + static constexpr size_t kGvcpForceIpBodyLength = 56; + } // namespace internal + + using GvcpFlag = uint8_t; // flag bits are specified by each command + + /** + * GVCP command defines the command values and the corresponding acknowledge values + * See more in the spec "18 Command and Acknowledge Values" + */ + enum class GvcpCommand : uint16_t + { + // Discovery Protocol Control + DiscoveredCmd = 0x0002, + DiscoveredAck = 0x0003, + ForceIpCmd = 0x0004, + ForceIpAck = 0x0005, + + // Streaming Protocol Control + PacketResendCmd = 0x0040, + PacketResendAck = 0x0041, // Resent packet must be on the stream channel + + // Device Memory Access + ReadRegCmd = 0x0080, + ReadRegAck = 0x0081, + WriteRegCmd = 0x0082, + WriteRegAck = 0x0083, + ReadMemCmd = 0x0084, + ReadMemAck = 0x0085, + WriteMemCmd = 0x0086, + WriteMemAck = 0x0087, + PendingAck = 0x0089, + + // Asynchronous Events + EventCmd = 0x00C0, + EventAck = 0x00C1, + EventDataCmd = 0x00C2, + EventDataAck = 0x00C3, + + // Miscellaneous + ActionCmd = 0x0100, + ActionAck = 0x0101, + Unknown = 0xFFFF + }; + + /// output operator for GvcpCommand + std::ostream& operator<<(std::ostream& os, GvcpCommand command); + + /** + * GVCP response status can be returned in an acknowledge message or a GVSP header. + * See more in the spec "Table 19-1: List of Standard Status Codes" + */ + enum class GvcpResponseStatus : uint16_t + { + Success = 0x0000, ///< Command executed successfully + PacketResend = 0x0100, ///< Only applies to packet being resent + NotImplemented = 0x8001, ///< Command is not supported by the device + InvalidParameter = + 0x8002, ///< At least one parameter provided in the command is invalid (or out of range) for the device + InvalidAddress = 0x8003, ///< An attempt was made to access a non-existent address space location. + WriteProtect = 0x8004, ///< The addressed register cannot be written to + BadAlignment = 0x8005, ///< A badly aligned address offset or data size was specified. + AccessDenied = + 0x8006, ///< An attempt was made to access an address location which is currently/momentary not accessible + Busy = 0x8007, ///< A required resource to service the request is not currently available. The request may be + ///< retried at a later time + LocalProblem = 0x8008, ///< deprecated + MsgMismatch = 0x8009, ///< deprecated + InvalidProtocol = 0x800A, ///< deprecated + NoMsg = 0x800B, ///< deprecated + PacketUnavailable = 0x800C, ///< The requested packet is not available anymore + DataOverrun = 0x800D, ///< Internal memory of GVSP transmitter overrun (typically for image acquisition) + InvalidHeader = 0x800E, ///< The message header is not valid. Some of its fields do not match the specification + WrongConfig = 0x800F, ///< deprecated + PacketNotYetAvailable = 0x8010, ///< The requested packet has not yet been acquired. Can be used for linescan + ///< cameras device when line trigger rate is slower than application timeout + PacketAndPrevRemovedFromMemory = 0x8011, ///< The requested packet and all previous ones are not available + ///< anymore and have been discarded from the GVSP transmitter memory + PacketRemovedFromMemory = 0x8012, ///< The requested packet is not available anymore and has been discarded + ///< from the GVSP transmitter memory + NoRefTime = 0x8013, ///< The device is not synchronized to a master clock to be used as time reference + PacketTemporarilyUnavailable = 0x8014, ///< The packet cannot be resent at the moment due to temporary + ///< bandwidth issues and should be requested again in the future + Overflow = 0x8015, ///< A device queue or packet data has overflowed + ActionLate = 0x8016, ///< The requested scheduled action command was requested at a time that is already past + LeaderTrailerOverflow = 0x8017, // GEV 2.1 + Error = 0x8FFF, ///< Generic error + Unknown = 0xFFFF ///< Unknown status + }; + + std::ostream& operator<<(std::ostream& os, GvcpResponseStatus status); + + namespace internal + { + +#pragma pack(push, 1) + /** + * GVCP request header + * refer to the spec "15.1 Request Header". The data is stored as big-endian. + */ + struct gvcp_request_header + { + uint8_t magicNumber = internal::kGvcpMagicNumber; ///< Magic number + uint8_t flag = + 0; ///< GVCP flag. 0-3 bits are specified by each command, 4-6 bits are reserved, 7 bit is acknowledge + uint16_t command = 0; ///< Command + uint16_t dataSize = 0; ///< Data size + uint16_t requestId = 0; ///< Request ID + + // ------------- methods -------------- + gvcp_request_header() = default; + + gvcp_request_header(GvcpFlag flag, GvcpCommand command, uint16_t dataSize, uint16_t requestId); + + GvcpCommand getCommand() const; + }; + static_assert(sizeof(gvcp_request_header) == internal::kGvcpRequestHeaderLength, + "GVCP request header size should be 8 bytes"); + + /** + * GVCP acknowledge header + * refer to the spec "15.2 Acknowledge Header". The data is stored as big-endian. + */ + struct gvcp_ack_header + { + uint16_t status = 0; ///< Response status + uint16_t command = 0; ///< Command + uint16_t dataSize = 0; ///< Data size + uint16_t ackId = 0; ///< Acknowledge ID + + // ------------- methods -------------- + gvcp_ack_header() = default; + + gvcp_ack_header(GvcpResponseStatus status, GvcpCommand command, uint16_t dataSize, uint16_t ackId); + + GvcpCommand getCommand() const; + }; + static_assert(sizeof(gvcp_ack_header) == internal::kGvcpAckHeaderLength, + "GVCP ack header size should be 8 bytes"); + + /** + * GVCP discovery acknowledge body + * refer to the spec "16.1.2 DISCOVERY_ACK". The data is stored as big-endian. + */ + struct gvcp_discovery_body + { + uint16_t versionMajor = 0; ///< GigE Vision version major number + uint16_t versionMinor = 0; ///< GigE Vision version minor number + uint32_t deviceMode = 0; ///< Device mode + uint16_t reserved = 0; ///< Reserved + uint8_t macAddress[6] = { 0 }; ///< MAC address + uint32_t supportedIpConfigOptions = 0; ///< Supported IP configuration options + uint32_t ipConfigCurrent = 0; ///< Current IP configuration + uint8_t reserved2[12] = { 0 }; ///< Reserved + uint32_t ipAddress = 0; ///< IP address + uint8_t reserved3[12]; ///< Reserved + uint32_t subnetMask = 0; ///< Subnet mask + uint8_t reserved4[12] = { 0 }; ///< Reserved + uint32_t defaultGateway = 0; ///< Default gateway + char manufacturerName[32] = { 0 }; ///< Manufacturer name + char modelName[32] = { 0 }; ///< Model name + char deviceVersion[32] = { 0 }; ///< Device version + char manufacturerSpecificInformation[48] = { 0 }; ///< Manufacturer specific information + char serialNumber[16] = { 0 }; ///< Serial number + char userDefinedName[16] = { 0 }; ///< User defined name + }; + static_assert(sizeof(gvcp_discovery_body) == internal::kGvcpDiscoveryBodyLength, + "GVCP ack body size should be 248 bytes"); + + /** + * GVCP force IP command body + * refer to the spec "16.2 FORCEIP". The data is stored as big-endian. + */ + struct gvcp_forceip_body + { + char padding1[2] = { 0 }; ///< Padding + char macAddress[6] = { 0 }; ///< MAC address + char padding2[12] = { 0 }; ///< Padding + uint32_t ipAddress = 0; ///< IP address + char padding3[12] = { 0 }; ///< Padding + uint32_t subnetMask = 0; ///< Subnet mask + char padding4[12] = { 0 }; ///< Padding + uint32_t gateway = 0; ///< Gateway + }; + static_assert(sizeof(gvcp_forceip_body) == internal::kGvcpForceIpBodyLength, + "GVCP force IP command body size should be 56 bytes"); +#pragma pack(pop) + } // namespace internal + + /** + * @class GvcpLayer + * A class representing the GigE Vision protocol(GVCP). + * The class is implemented according to the GigE Vision specification 2.0. + * @see https://en.wikipedia.org/wiki/GigE_Vision + * @note The class cannot be instantiated directly. + */ + class GvcpLayer : public Layer + { + public: + /** + * A static method that checks whether the port is considered as GVCP + * @param[in] port The port number to be checked + */ + static bool isGvcpPort(uint16_t port) + { + return port == 3956; + } + + /** + * Get the magic number + * @return uint8_t The magic number + */ + static bool verifyRequest(const uint8_t* data) + { + return data[0] == internal::kGvcpMagicNumber; + }; + + /** + * Construct a new GvcpLayer object + * @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 Layer* A pointer to the constructed GvcpLayer object + */ + static Layer* parseGvcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + // implement Layer's abstract methods + void parseNextLayer() override + {} + + // implement Layer's abstract methods + void computeCalculateFields() override + {} + + // implement Layer's abstract methods + OsiModelLayer getOsiModelLayer() const override + { + return OsiModelLayer::OsiModelApplicationLayer; + } + + protected: + GvcpLayer() = default; + + /** + * Construct a new GvcpLayer object + * @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 + */ + GvcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + }; + + /** + * GVCP request layer + */ + class GvcpRequestLayer : public GvcpLayer + { + public: + /** + * Construct a new GvcpLayer object + * @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 + */ + GvcpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * Construct a new GvcpRequestLayer object + * @param[in] data A pointer to the data including the header and the payload + * @param[in] dataSize The size of the data in bytes + */ + GvcpRequestLayer(const uint8_t* data, size_t dataSize); + + /** + * Construct a new GvcpRequestLayer object + * @param[in] command The command + * @param[in] payloadData A pointer to the payload data, optional + * @param[in] payloadDataSize The size of the payload data in bytes, optional + * @param[in] flag The flag, optional + * @param[in] requestId The request ID, it should be always larger than 1, optional + * @note all the parameters will be converted to the network byte order + */ + explicit GvcpRequestLayer(GvcpCommand command, const uint8_t* payloadData = nullptr, + uint16_t payloadDataSize = 0, GvcpFlag flag = 0, uint16_t requestId = 1); + + /** + * Get the flag from the header + */ + GvcpFlag getFlag() const + { + return getGvcpHeader()->flag; + } + + /** + * Get the data size from the header + */ + uint16_t getDataSize() const; + + /** + * Get the request ID from the header + */ + uint16_t getRequestId() const; + + /** + * Get the command from the header + */ + GvcpCommand getCommand() const; + + /** + * Verify the magic number in the header + * @return true The magic number is valid + */ + bool verifyMagicNumber() const + { + return getGvcpHeader()->magicNumber == internal::kGvcpMagicNumber; + } + + /** + * Check if the acknowledge is required from the header + * @return true The acknowledge is required + */ + bool hasAcknowledgeFlag() const + { + constexpr GvcpFlag kAcknowledgeFlag = 0b0000001; + return (getGvcpHeader()->flag & kAcknowledgeFlag) == kAcknowledgeFlag; + } + + // implement Layer's abstract methods + std::string toString() const override; + + // implement Layer's abstract methods + size_t getHeaderLen() const override + { + return sizeof(internal::gvcp_request_header); + } + + private: + /** + * Get the header object + * @return internal::gvcp_request_header* A pointer to the header object + */ + internal::gvcp_request_header* getGvcpHeader() const + { + return reinterpret_cast( + m_Data); // the header is at the beginning of the data + } + }; + + /** + * GVCP acknowledge layer + */ + class GvcpAcknowledgeLayer : public GvcpLayer + { + public: + /** + * Construct a new GvcpLayer object + * @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 + */ + GvcpAcknowledgeLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * Construct a new GvcpRequestLayer object + * @param[in] data A pointer to the data including the header and the payload + * @param[in] dataSize The size of the data in bytes + */ + GvcpAcknowledgeLayer(const uint8_t* data, size_t dataSize); + + /** + * Construct a new GvcpAcknowledgeLayer object + * @param[in] status The response status + * @param[in] command The command + * @param[in] payloadData A pointer to the payload data, optional + * @param[in] payloadDataSize The size of the payload data in bytes, optional + * @param[in] ackId The acknowledge ID, optional + * @note all the parameters will be converted to the network byte order + */ + GvcpAcknowledgeLayer(GvcpResponseStatus status, GvcpCommand command, const uint8_t* payloadData = nullptr, + uint16_t payloadDataSize = 0, uint16_t ackId = 0); + + /** + * @return the response status from the header + */ + GvcpResponseStatus getStatus() const; + + /** + * @return the response command type from the header + */ + GvcpCommand getCommand() const; + + /** + * @return the size of the data in bytes from the header + */ + uint16_t getDataSize() const; + + /** + * @return uint16_t The acknowledge ID from the header + */ + uint16_t getAckId() const; + + // implement Layer's abstract methods + std::string toString() const override; + + // implement Layer's abstract methods + size_t getHeaderLen() const override + { + return sizeof(internal::gvcp_ack_header); + } + + private: + /** + * Get the header object + * @return gvcp_ack_header* A pointer to the header object + */ + internal::gvcp_ack_header* getGvcpHeader() const + { + return reinterpret_cast(m_Data); // the header is at the beginning of the data + } + }; + + // ---------------------------------------- Special Layer ---------------------------------------- + + /** + * GVCP discovery request layer + */ + class GvcpDiscoveryRequestLayer : public GvcpRequestLayer + { + public: + /** + * Construct a new GvcpLayer object + * @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 + */ + GvcpDiscoveryRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : GvcpRequestLayer(data, dataLen, prevLayer, packet) {}; + + /** + * Construct a new GvcpRequestLayer object + * @param[in] data A pointer to the data including the header and the payload + * @param[in] dataSize The size of the data in bytes + */ + explicit GvcpDiscoveryRequestLayer(const uint8_t* data, size_t dataSize) : GvcpRequestLayer(data, dataSize) {}; + + /** + * Construct a new GvcpRequestLayer object + * @param[in] payloadData A pointer to the payload data, optional + * @param[in] payloadDataSize The size of the payload data in bytes, optional + * @param[in] flag The flag, optional + * @param[in] requestId The request ID, it should be always larger than 1, optional + * @note all the parameters will be converted to the network byte order + */ + explicit GvcpDiscoveryRequestLayer(const uint8_t* payloadData = nullptr, uint16_t payloadDataSize = 0, + GvcpFlag flag = 0, uint16_t requestId = 1) + : GvcpRequestLayer(GvcpCommand::DiscoveredCmd, payloadData, payloadDataSize, flag, requestId) {}; + }; + + /** + * GVCP discovery acknowledge layer + */ + class GvcpDiscoveryAcknowledgeLayer : public GvcpAcknowledgeLayer + { + public: + /** + * Construct a new GvcpLayer object + * @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 + */ + GvcpDiscoveryAcknowledgeLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : GvcpAcknowledgeLayer(data, dataLen, prevLayer, packet) {}; + + /** + * Construct a new GvcpRequestLayer object + * @param[in] data A pointer to the data including the header and the payload + * @param[in] dataSize The size of the data in bytes + */ + GvcpDiscoveryAcknowledgeLayer(const uint8_t* data, uint16_t dataSize) : GvcpAcknowledgeLayer(data, dataSize) {}; + + /** + * Construct a new GvcpAcknowledgeLayer object + * @param[in] status The response status + * @param[in] payloadData A pointer to the payload data, optional + * @param[in] payloadDataSize The size of the payload data in bytes, optional + * @param[in] ackId The acknowledge ID, optional + * @note all the parameters will be converted to the network byte order + */ + explicit GvcpDiscoveryAcknowledgeLayer(GvcpResponseStatus status, const uint8_t* payloadData = nullptr, + uint16_t payloadDataSize = 0, uint16_t ackId = 0) + : GvcpAcknowledgeLayer(status, GvcpCommand::DiscoveredAck, payloadData, payloadDataSize, ackId) {}; + + /** + * Get the version + * @return std::pair The version major and minor + */ + std::pair getVersion() const + { + auto body = this->getGvcpDiscoveryBody(); + return { body->versionMajor, body->versionMinor }; + } + + /** + * Get the IP address + * @return pcpp::IPAddress The IP address. Throw if the IP address is invalid. + */ + pcpp::MacAddress getMacAddress() const + { + auto body = this->getGvcpDiscoveryBody(); + return pcpp::MacAddress(body->macAddress); + } + + /** + * Get the IP address + * @return pcpp::IPAddress The IP address. Throw if the IP address is invalid. + */ + pcpp::IPv4Address getIpAddress() const + { + auto body = this->getGvcpDiscoveryBody(); + return pcpp::IPv4Address(body->ipAddress); + } + + /** + * Get the subnet mask + * @return pcpp::IPAddress The subnet mask. Throw if the subnet mask is invalid. + */ + pcpp::IPv4Address getSubnetMask() const + { + auto body = this->getGvcpDiscoveryBody(); + return pcpp::IPv4Address(body->subnetMask); + } + + /** + * Get the gateway IP address + * @return pcpp::IPAddress The gateway IP address. Throw if the gateway IP address is invalid. + */ + pcpp::IPv4Address getGatewayIpAddress() const + { + auto body = this->getGvcpDiscoveryBody(); + return pcpp::IPv4Address(body->defaultGateway); + } + + /** + * Get the manufacturer name + * @return std::string The manufacturer name + */ + std::string getManufacturerName() const + { + auto body = this->getGvcpDiscoveryBody(); + return std::string(body->manufacturerName); + } + + /** + * Get the model name + * @return std::string The model name + */ + std::string getModelName() const + { + auto body = this->getGvcpDiscoveryBody(); + return std::string(body->modelName); + } + + /** + * Get the device version + * @return std::string The device version + */ + std::string getDeviceVersion() const + { + auto body = this->getGvcpDiscoveryBody(); + return std::string(body->deviceVersion); + } + + /** + * Get the manufacturer specific information + * @return std::string The manufacturer specific information + */ + std::string getManufacturerSpecificInformation() const + { + auto body = this->getGvcpDiscoveryBody(); + return std::string(body->manufacturerSpecificInformation); + } + + /** + * Get the serial number + * @return std::string The serial number + */ + std::string getSerialNumber() const + { + auto body = this->getGvcpDiscoveryBody(); + return std::string(body->serialNumber); + } + + /** + * Get the user defined name + * @return std::string The user defined name + */ + std::string getUserDefinedName() const + { + auto body = this->getGvcpDiscoveryBody(); + return std::string(body->userDefinedName); + } + + private: + internal::gvcp_discovery_body* getGvcpDiscoveryBody() const + { + return reinterpret_cast(m_Data + getHeaderLen()); + } + }; + + /** + * GVCP force IP request layer + */ + class GvcpForceIpRequestLayer : public GvcpRequestLayer + { + public: + /** + * Construct a new GvcpLayer object + * @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 + */ + GvcpForceIpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : GvcpRequestLayer(data, dataLen, prevLayer, packet) {}; + + /** + * Construct a new GvcpRequestLayer object + * @param[in] data A pointer to the data including the header and the payload + * @param[in] dataSize The size of the data in bytes + */ + explicit GvcpForceIpRequestLayer(const uint8_t* data, size_t dataSize) : GvcpRequestLayer(data, dataSize) {}; + + /** + * Construct a new GvcpRequestLayer object + * @param[in] payloadData A pointer to the payload data, optional + * @param[in] payloadDataSize The size of the payload data in bytes, optional + * @param[in] flag The flag, optional + * @param[in] requestId The request ID, it should be always larger than 1, optional + * @note all the parameters will be converted to the network byte order + */ + explicit GvcpForceIpRequestLayer(const uint8_t* payloadData = nullptr, uint16_t payloadDataSize = 0, + GvcpFlag flag = 0, uint16_t requestId = 1) + : GvcpRequestLayer(GvcpCommand::ForceIpCmd, payloadData, payloadDataSize, flag, requestId) {}; + + /** + * Get the IP address + * @return pcpp::IPAddress The IP address. Throw if the IP address is invalid. + */ + pcpp::MacAddress getMacAddress() const + { + auto body = this->getGvcpForceIpBody(); + return pcpp::MacAddress(reinterpret_cast(body->macAddress)); + } + + /** + * Get the IP address + * @return pcpp::IPAddress The IP address. Throw if the IP address is invalid. + */ + pcpp::IPv4Address getIpAddress() const + { + auto body = this->getGvcpForceIpBody(); + return pcpp::IPv4Address(body->ipAddress); + } + + /** + * Get the subnet mask + * @return pcpp::IPAddress The subnet mask. Throw if the subnet mask is invalid. + */ + pcpp::IPv4Address getSubnetMask() const + { + auto body = this->getGvcpForceIpBody(); + return pcpp::IPv4Address(body->subnetMask); + } + + /** + * Get the gateway IP address + * @return pcpp::IPAddress The gateway IP address. Throw if the gateway IP address is invalid. + */ + pcpp::IPv4Address getGatewayIpAddress() const + { + auto body = this->getGvcpForceIpBody(); + return pcpp::IPv4Address(body->gateway); + } + + private: + internal::gvcp_forceip_body* getGvcpForceIpBody() const + { + return reinterpret_cast(m_Data + getHeaderLen()); + } + }; + + /** + * GVCP force IP acknowledge layer + */ + class GvcpForceIpAcknowledgeLayer : public GvcpAcknowledgeLayer + { + public: + /** + * Construct a new GvcpLayer object + * @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 + */ + GvcpForceIpAcknowledgeLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : GvcpAcknowledgeLayer(data, dataLen, prevLayer, packet) {}; + + /** + * Construct a new GvcpRequestLayer object + * @param[in] data A pointer to the data including the header and the payload + * @param[in] dataSize The size of the data in bytes + */ + GvcpForceIpAcknowledgeLayer(const uint8_t* data, size_t dataSize) : GvcpAcknowledgeLayer(data, dataSize) {}; + + /** + * Construct a new GvcpAcknowledgeLayer object + * @param[in] status The response status + * @param[in] payloadData A pointer to the payload data, optional + * @param[in] payloadDataSize The size of the payload data in bytes, optional + * @param[in] ackId The acknowledge ID, optional + * @note all the parameters will be converted to the network byte order + */ + explicit GvcpForceIpAcknowledgeLayer(GvcpResponseStatus status, const uint8_t* payloadData = nullptr, + uint16_t payloadDataSize = 0, uint16_t ackId = 0) + : GvcpAcknowledgeLayer(status, GvcpCommand::ForceIpAck, payloadData, payloadDataSize, ackId) {}; + }; +} // namespace pcpp diff --git a/Packet++/header/ProtocolType.h b/Packet++/header/ProtocolType.h index f853b63f88..6226726172 100644 --- a/Packet++/header/ProtocolType.h +++ b/Packet++/header/ProtocolType.h @@ -352,6 +352,11 @@ namespace pcpp */ const ProtocolType WireGuard = 56; + /* + * GVCP protocol + */ + const ProtocolType GVCP = 57; + /** * GTPv2 protocol */ diff --git a/Packet++/src/GvcpLayer.cpp b/Packet++/src/GvcpLayer.cpp new file mode 100644 index 0000000000..7b486bd917 --- /dev/null +++ b/Packet++/src/GvcpLayer.cpp @@ -0,0 +1,251 @@ +#define LOG_MODULE PacketLogModuleGvcpLayer + +#include "GvcpLayer.h" +#include "PayloadLayer.h" +#include "SystemUtils.h" +#include +#include +#include +#include + +template <> struct std::hash +{ + std::size_t operator()(const pcpp::GvcpCommand& command) const + { + return static_cast(command); + } +}; + +namespace pcpp +{ + namespace internal + { + std::unordered_set gvcpCommandSet = { + GvcpCommand::DiscoveredCmd, GvcpCommand::DiscoveredAck, GvcpCommand::ForceIpCmd, + GvcpCommand::ForceIpAck, GvcpCommand::PacketResendCmd, GvcpCommand::PacketResendAck, + GvcpCommand::ReadRegCmd, GvcpCommand::ReadRegAck, GvcpCommand::WriteRegCmd, + GvcpCommand::WriteRegAck, GvcpCommand::ReadMemCmd, GvcpCommand::ReadMemAck, + GvcpCommand::WriteMemCmd, GvcpCommand::WriteMemAck, GvcpCommand::PendingAck, + GvcpCommand::EventCmd, GvcpCommand::EventAck, GvcpCommand::EventDataCmd, + GvcpCommand::EventDataAck, GvcpCommand::ActionCmd, GvcpCommand::ActionAck, + GvcpCommand::Unknown + }; + + gvcp_request_header::gvcp_request_header(GvcpFlag flag, GvcpCommand command, uint16_t dataSize, + uint16_t requestId) + : flag(flag), command(hostToNet16(static_cast(command))), dataSize(hostToNet16(dataSize)), + requestId(hostToNet16(requestId)) + {} + + GvcpCommand gvcp_request_header::getCommand() const + { + GvcpCommand command_ = static_cast(netToHost16(command)); + if (internal::gvcpCommandSet.find(command_) != internal::gvcpCommandSet.end()) + { + return command_; + } + + return GvcpCommand::Unknown; + } + + gvcp_ack_header::gvcp_ack_header(GvcpResponseStatus status, GvcpCommand command, uint16_t dataSize, + uint16_t ackId) + : status(hostToNet16(static_cast(status))), command(hostToNet16(static_cast(command))), + dataSize(hostToNet16(dataSize)), ackId(hostToNet16(ackId)) + {} + + GvcpCommand gvcp_ack_header::getCommand() const + { + GvcpCommand command_ = static_cast(netToHost16(command)); + if (internal::gvcpCommandSet.find(command_) != internal::gvcpCommandSet.end()) + { + return command_; + } + + return GvcpCommand::Unknown; + } + + } // namespace internal + + std::ostream& operator<<(std::ostream& os, GvcpCommand command) + { + os << "0x" << std::hex << static_cast(command) << std::dec; + return os; + } + + std::ostream& operator<<(std::ostream& os, GvcpResponseStatus status) + { + os << "0x" << std::hex << static_cast(status) << std::dec; + return os; + } + + /*---------------------- Class GvcpLayer ----------------------------*/ + + GvcpLayer::GvcpLayer(uint8_t* data, size_t dataSize, Layer* prevLayer, Packet* packet) + : Layer(data, dataSize, prevLayer, packet, GVCP) + {} + + Layer* GvcpLayer::parseGvcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + { + if (data == nullptr || dataLen < sizeof(internal::gvcp_request_header) || + dataLen < sizeof(internal::gvcp_ack_header)) + { + return new PayloadLayer(data, dataLen, prevLayer, packet); + } + + if (GvcpLayer::verifyRequest(data)) + { + auto* header = reinterpret_cast(data); + switch (header->getCommand()) + { + case GvcpCommand::DiscoveredCmd: + return new GvcpDiscoveryRequestLayer(data, dataLen, prevLayer, packet); + case GvcpCommand::ForceIpCmd: + return new GvcpForceIpRequestLayer(data, dataLen, prevLayer, packet); + default: + return new GvcpRequestLayer(data, dataLen, prevLayer, packet); + } + } + else + { + auto* header = reinterpret_cast(data); + switch (header->getCommand()) + { + case GvcpCommand::DiscoveredAck: + return new GvcpDiscoveryAcknowledgeLayer(data, dataLen); + case GvcpCommand::ForceIpAck: + return new GvcpForceIpAcknowledgeLayer(data, dataLen); + default: + return new GvcpAcknowledgeLayer(data, dataLen, prevLayer, packet); + } + } + } + + /*---------------------- Class GvcpRequestLayer ----------------------------*/ + GvcpRequestLayer::GvcpRequestLayer(uint8_t* data, size_t dataSize, Layer* prevLayer, Packet* packet) + : GvcpLayer(data, dataSize, prevLayer, packet) + {} + + GvcpRequestLayer::GvcpRequestLayer(GvcpCommand command, const uint8_t* payloadData, uint16_t payloadDataSize, + GvcpFlag flag, uint16_t requestId) + { + m_Protocol = GVCP; + + m_DataLen = getHeaderLen() + payloadDataSize; + m_Data = new uint8_t[m_DataLen]; + + // copy the payload data + memcpy(m_Data + getHeaderLen(), payloadData, payloadDataSize); + + // set the header fields + auto header = reinterpret_cast(m_Data); + header->command = hostToNet16(static_cast(command)); + header->flag = flag; + header->requestId = hostToNet16(requestId); + header->dataSize = hostToNet16(payloadDataSize); + } + + GvcpRequestLayer::GvcpRequestLayer(const uint8_t* data, size_t dataSize) + { + m_Protocol = GVCP; + + m_DataLen = dataSize; + m_Data = new uint8_t[m_DataLen]; + memcpy(m_Data, data, m_DataLen); + } + + std::string GvcpRequestLayer::toString() const + { + std::stringstream ss; + ss << "GVCP Request Layer, Command: " << getCommand() << ", Request ID: " << getRequestId(); + return ss.str(); + } + + uint16_t GvcpRequestLayer::getDataSize() const + { + return netToHost16(getGvcpHeader()->dataSize); + } + + uint16_t GvcpRequestLayer::getRequestId() const + { + return netToHost16(getGvcpHeader()->requestId); + } + + GvcpCommand GvcpRequestLayer::getCommand() const + { + GvcpCommand command = getGvcpHeader()->getCommand(); + if (internal::gvcpCommandSet.find(command) != internal::gvcpCommandSet.end()) + { + return command; + } + + return GvcpCommand::Unknown; + } + + /*---------------------- Class GvcpAcknowledgeLayer ----------------------------*/ + GvcpAcknowledgeLayer::GvcpAcknowledgeLayer(uint8_t* data, size_t dataSize, Layer* prevLayer, Packet* packet) + : GvcpLayer(data, dataSize, prevLayer, packet) + {} + + GvcpAcknowledgeLayer::GvcpAcknowledgeLayer(GvcpResponseStatus status, GvcpCommand command, + const uint8_t* payloadData, uint16_t payloadDataSize, uint16_t ackId) + { + m_Protocol = GVCP; + + m_DataLen = getHeaderLen() + payloadDataSize; + m_Data = new uint8_t[m_DataLen]; + + // copy the payload data + memcpy(m_Data + getHeaderLen(), payloadData, payloadDataSize); + + // set the header fields + auto header = reinterpret_cast(m_Data); + header->status = hostToNet16(static_cast(status)); + header->command = hostToNet16(static_cast(command)); + header->dataSize = hostToNet16(payloadDataSize); + header->ackId = hostToNet16(ackId); + } + + GvcpAcknowledgeLayer::GvcpAcknowledgeLayer(const uint8_t* data, size_t dataSize) + { + m_Protocol = GVCP; + + m_DataLen = dataSize; + m_Data = new uint8_t[m_DataLen]; + memcpy(m_Data, data, m_DataLen); + } + + std::string GvcpAcknowledgeLayer::toString() const + { + std::stringstream ss; + ss << "GVCP Acknowledge Layer, Command: " << getCommand() << ", Acknowledge ID: " << getAckId() + << ", Status: " << getStatus(); + return ss.str(); + } + + GvcpResponseStatus GvcpAcknowledgeLayer::getStatus() const + { + return static_cast((netToHost16(getGvcpHeader()->status))); + } + + GvcpCommand GvcpAcknowledgeLayer::getCommand() const + { + GvcpCommand command = getGvcpHeader()->getCommand(); + if (internal::gvcpCommandSet.find(command) != internal::gvcpCommandSet.end()) + { + return command; + } + + return GvcpCommand::Unknown; + } + + uint16_t GvcpAcknowledgeLayer::getDataSize() const + { + return netToHost16(getGvcpHeader()->dataSize); + } + + uint16_t GvcpAcknowledgeLayer::getAckId() const + { + return netToHost16(getGvcpHeader()->ackId); + } +} // namespace pcpp diff --git a/Packet++/src/UdpLayer.cpp b/Packet++/src/UdpLayer.cpp index 9cfaa2cbe0..46561988c3 100644 --- a/Packet++/src/UdpLayer.cpp +++ b/Packet++/src/UdpLayer.cpp @@ -12,6 +12,7 @@ #include "SipLayer.h" #include "RadiusLayer.h" #include "GtpLayer.h" +#include "GvcpLayer.h" #include "NtpLayer.h" #include "SomeIpLayer.h" #include "WakeOnLanLayer.h" @@ -144,6 +145,10 @@ namespace pcpp if (!m_NextLayer) m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); } + else if (GvcpLayer::isGvcpPort(portSrc) || GvcpLayer::isGvcpPort(portDst)) + { + m_NextLayer = GvcpLayer::parseGvcpLayer(udpData, udpDataLen, this, m_Packet); + } else m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); } diff --git a/README.md b/README.md index 5bd05a0b9d..6e372da15e 100644 --- a/README.md +++ b/README.md @@ -282,7 +282,8 @@ PcapPlusPlus currently supports parsing, editing and creation of packets of the 47. SOME/IP 48. SSH - parsing only (no editing capabilities) 49. Telnet - parsing only (no editing capabilities) -50. Generic payload +50. GVCP +51. Generic payload ## DPDK And PF_RING Support diff --git a/Tests/Packet++Test/CMakeLists.txt b/Tests/Packet++Test/CMakeLists.txt index 0bcac69e63..c37a31ec9e 100644 --- a/Tests/Packet++Test/CMakeLists.txt +++ b/Tests/Packet++Test/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable( Tests/FtpTests.cpp Tests/GreTests.cpp Tests/GtpTests.cpp + Tests/GvcpTests.cpp Tests/HttpTests.cpp Tests/IcmpTests.cpp Tests/IcmpV6Tests.cpp diff --git a/Tests/Packet++Test/PacketExamples/gvcp_all.pcap b/Tests/Packet++Test/PacketExamples/gvcp_all.pcap new file mode 100644 index 0000000000..988de4f26f Binary files /dev/null and b/Tests/Packet++Test/PacketExamples/gvcp_all.pcap differ diff --git a/Tests/Packet++Test/PacketExamples/gvcp_discovery_ack.dat b/Tests/Packet++Test/PacketExamples/gvcp_discovery_ack.dat new file mode 100644 index 0000000000..ed8778944f --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gvcp_discovery_ack.dat @@ -0,0 +1 @@ +aabbccddeeff00112233445508004500011c7cc340004011ec6fac1c3c64ac1c3c010f74945b010812410000000300f800010002000180000000000000044beab0b40000000700000005000000000000000000000000ac1c3c64000000000000000000000000ffffff00000000000000000000000000ac1c3c0156656e646f7230310000000000000000000000000000000000000000000000004142434445203344205363616e6e657220285457290000000000000000000000584c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005858582d30303500000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/gvcp_discovery_cmd.dat b/Tests/Packet++Test/PacketExamples/gvcp_discovery_cmd.dat new file mode 100644 index 0000000000..b85c8b6e2f --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gvcp_discovery_cmd.dat @@ -0,0 +1 @@ +aabbccddeeff001122334455080045000024a98040004011a92bac1c3c01ffffffffeb320f740010e83e4211000200004de6 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/gvcp_forceip_ack.dat b/Tests/Packet++Test/PacketExamples/gvcp_forceip_ack.dat new file mode 100644 index 0000000000..bc8bbbfcc6 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gvcp_forceip_ack.dat @@ -0,0 +1 @@ +aabbccddeeff001122334455080045000024000800008011b36ec0a80501c0a801010f740f7400100000000000050000225300000000000000000000 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/gvcp_forceip_cmd.dat b/Tests/Packet++Test/PacketExamples/gvcp_forceip_cmd.dat new file mode 100644 index 0000000000..d777bb2b75 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gvcp_forceip_cmd.dat @@ -0,0 +1 @@ +aabbccddeeff00112233445508004500005c06a7400040117241c0a80101ffffffff0f740f740048c202420100040038225300008ce9b40163b2000000000000000000000000c0a80501000000000000000000000000ffff000000000000000000000000000000000000 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/gvcp_readreg_ack.dat b/Tests/Packet++Test/PacketExamples/gvcp_readreg_ack.dat new file mode 100644 index 0000000000..1167053bf2 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gvcp_readreg_ack.dat @@ -0,0 +1 @@ +aabbccddeeff001122334455080045000028000d00008011b365c0a80501c0a801010f740f74001400000000008100041fee80000001000000000000 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/gvcp_readreg_cmd.dat b/Tests/Packet++Test/PacketExamples/gvcp_readreg_cmd.dat new file mode 100644 index 0000000000..2d6af60931 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gvcp_readreg_cmd.dat @@ -0,0 +1 @@ +aabbccddeeff001122334455080045000028db7940004011d7f8c0a80101c0a805010f740f74001487784201008000048bf000000000 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/gvcp_writereg_ack.dat b/Tests/Packet++Test/PacketExamples/gvcp_writereg_ack.dat new file mode 100644 index 0000000000..ff1135d95f --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gvcp_writereg_ack.dat @@ -0,0 +1 @@ +aabbccddeeff001122334455080045000024000900008011b36dc0a80501c0a801010f740f7400100000800600830000225400000000000000000000 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/gvcp_writereg_cmd.dat b/Tests/Packet++Test/PacketExamples/gvcp_writereg_cmd.dat new file mode 100644 index 0000000000..eca924993d --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/gvcp_writereg_cmd.dat @@ -0,0 +1 @@ +aabbccddeeff0011223344550800450000449926400040111a30c0a80101c0a805010f740f7400308794420100820020225400000a00000000020000064cc0a805010000065cffff00000000001400000005 \ No newline at end of file diff --git a/Tests/Packet++Test/TestDefinition.h b/Tests/Packet++Test/TestDefinition.h index 9047e2682d..932dd2feea 100644 --- a/Tests/Packet++Test/TestDefinition.h +++ b/Tests/Packet++Test/TestDefinition.h @@ -168,6 +168,17 @@ PTF_TEST_CASE(GtpV2LayerParsingTest); PTF_TEST_CASE(GtpV2LayerCreationTest); PTF_TEST_CASE(GtpV2LayerEditTest); +// Implemented in GvcpTests.cpp +PTF_TEST_CASE(GvcpBasicTest); +PTF_TEST_CASE(GvcpDiscoveryAck); +PTF_TEST_CASE(GvcpForceIpCommand); +PTF_TEST_CASE(GvcpDiscoveryCommand); +PTF_TEST_CASE(GvcpForceIpAck); +PTF_TEST_CASE(GvcpReadRegisterCommand); +PTF_TEST_CASE(GvcpReadRegisterAcknowledge); +PTF_TEST_CASE(GvcpWriteRegisterCommand); +PTF_TEST_CASE(GvcpWriteRegisterAcknowledge); + // Implemented in BgpTests.cpp PTF_TEST_CASE(BgpLayerParsingTest); PTF_TEST_CASE(BgpLayerCreationTest); diff --git a/Tests/Packet++Test/Tests/GvcpTests.cpp b/Tests/Packet++Test/Tests/GvcpTests.cpp new file mode 100644 index 0000000000..83aed58045 --- /dev/null +++ b/Tests/Packet++Test/Tests/GvcpTests.cpp @@ -0,0 +1,439 @@ +#include "../TestDefinition.h" +#include "../Utils/TestUtils.h" +#include "GeneralUtils.h" +#include "GvcpLayer.h" +#include "Packet.h" +#include "SystemUtils.h" +#include "UdpLayer.h" +#include + +PTF_TEST_CASE(GvcpBasicTest) +{ + using namespace pcpp; + + { + std::vector payload = { 0x00, 0x01, 0x02, 0x03 }; + GvcpRequestLayer gvcpRequestLayer(GvcpCommand::DiscoveredCmd, payload.data(), payload.size(), 1, 2); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getProtocol(), GVCP); + + PTF_ASSERT_EQUAL(gvcpRequestLayer.getCommand(), GvcpCommand::DiscoveredCmd); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getFlag(), 1); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getRequestId(), 2); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getDataSize(), payload.size()); + } + { + std::vector payload = { 0x00, 0x01, 0x02, 0x03 }; + GvcpAcknowledgeLayer gvcpAcknowledgeLayer(GvcpResponseStatus::Success, GvcpCommand::DiscoveredAck, + payload.data(), payload.size(), 2); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getStatus(), GvcpResponseStatus::Success); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getCommand(), GvcpCommand::DiscoveredAck); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getAckId(), 2); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getDataSize(), payload.size()); + } +} + +PTF_TEST_CASE(GvcpDiscoveryCommand) +{ + // test the creation from the raw buffer + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_discovery_cmd.dat"); + pcpp::Packet discoverRequestPacket(&rawPacket1); + + auto udpLayer = discoverRequestPacket.getLayerOfType(); + + // we get the raw buffer from the payload of the UDP layer and create a gvcpRequestLayer from the buffer + GvcpDiscoveryRequestLayer gvcpRequestLayer(udpLayer->getLayerPayload(), udpLayer->getLayerPayloadSize()); + + PTF_ASSERT_EQUAL(gvcpRequestLayer.getProtocol(), GVCP); + PTF_ASSERT_EQUAL(uint8_t(gvcpRequestLayer.getFlag()), uint8_t(0x11)); // allow broadcast, acknowledge required + PTF_ASSERT_EQUAL(gvcpRequestLayer.hasAcknowledgeFlag(), true); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getCommand(), GvcpCommand::DiscoveredCmd); + PTF_ASSERT_EQUAL(gvcpRequestLayer.verifyMagicNumber(), true); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getDataSize(), gvcpRequestLayer.getLayerPayloadSize()); + } + + // test the GVCP layer directly from the packet + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_discovery_cmd.dat"); + pcpp::Packet discoverCmdPacket(&rawPacket1); + + // we get the GVCP layer from the packet + auto gvcpRequestLayer = discoverCmdPacket.getLayerOfType(); + + PTF_ASSERT_EQUAL(gvcpRequestLayer->getProtocol(), GVCP); + PTF_ASSERT_EQUAL(uint8_t(gvcpRequestLayer->getFlag()), uint8_t(0x11)); // allow broadcast, acknowledge required + PTF_ASSERT_EQUAL(gvcpRequestLayer->hasAcknowledgeFlag(), true); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getCommand(), GvcpCommand::DiscoveredCmd); + PTF_ASSERT_EQUAL(gvcpRequestLayer->verifyMagicNumber(), true); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getDataSize(), gvcpRequestLayer->getLayerPayloadSize()); + } +} + +PTF_TEST_CASE(GvcpDiscoveryAck) +{ + // test the creation from the raw buffer + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_discovery_ack.dat"); + pcpp::Packet discoverAckPacket(&rawPacket1); + + auto udpLayer = discoverAckPacket.getLayerOfType(); + + // we get the raw buffer from the payload of the UDP layer and create a GvcpAcknowledgeLayer from the buffer + GvcpDiscoveryAcknowledgeLayer gvcpAcknowledgeLayer(udpLayer->getLayerPayload(), + udpLayer->getLayerPayloadSize()); + + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getStatus(), GvcpResponseStatus::Success); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getCommand(), GvcpCommand::DiscoveredAck); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getAckId(), 1); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getDataSize(), + udpLayer->getLayerPayloadSize() - sizeof(internal::gvcp_ack_header)); + + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getMacAddress(), pcpp::MacAddress("00:04:4b:ea:b0:b4")); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getIpAddress(), pcpp::IPv4Address("172.28.60.100")); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getManufacturerName(), "Vendor01"); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getModelName(), "ABCDE 3D Scanner (TW)"); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getSerialNumber(), "XXX-005"); + } + + // test the GVCP layer directly from the packet + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_discovery_ack.dat"); + pcpp::Packet discoverAckPacket(&rawPacket1); + + // we get the GVCP layer from the packet + auto gvcpAcknowledgeLayer = discoverAckPacket.getLayerOfType(); + + PTF_ASSERT_NOT_NULL(gvcpAcknowledgeLayer); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getStatus(), GvcpResponseStatus::Success); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getCommand(), GvcpCommand::DiscoveredAck); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getAckId(), 1); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getDataSize(), gvcpAcknowledgeLayer->getLayerPayloadSize()); + + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getMacAddress(), pcpp::MacAddress("00:04:4b:ea:b0:b4")); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getIpAddress(), pcpp::IPv4Address("172.28.60.100")); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getManufacturerName(), "Vendor01"); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getModelName(), "ABCDE 3D Scanner (TW)"); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getSerialNumber(), "XXX-005"); + } +} + +PTF_TEST_CASE(GvcpForceIpCommand) +{ + // test the creation from the raw buffer + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_forceip_cmd.dat"); + pcpp::Packet discoverAckPacket(&rawPacket1); + + auto udpLayer = discoverAckPacket.getLayerOfType(); + + // we get the raw buffer from the payload of the UDP layer and create a GvcpRequestLayer from the buffer + GvcpForceIpRequestLayer gvcpRequestLayer(udpLayer->getLayerPayload(), udpLayer->getLayerPayloadSize()); + + PTF_ASSERT_EQUAL(gvcpRequestLayer.getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getFlag(), 0x01); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getCommand(), GvcpCommand::ForceIpCmd); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getDataSize(), + udpLayer->getLayerPayloadSize() - sizeof(internal::gvcp_request_header)); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getRequestId(), 8787); + + PTF_ASSERT_EQUAL(gvcpRequestLayer.getMacAddress(), pcpp::MacAddress("8c:e9:b4:01:63:b2")); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getIpAddress(), pcpp::IPv4Address("192.168.5.1")); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getSubnetMask(), pcpp::IPv4Address("255.255.0.0")); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getGatewayIpAddress(), pcpp::IPv4Address("0.0.0.0")); + } + + // test the GVCP layer directly from the packet + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_forceip_cmd.dat"); + pcpp::Packet forceIpCommandPacket(&rawPacket1); + + // we get the GVCP layer from the packet + auto gvcpRequestLayer = forceIpCommandPacket.getLayerOfType(); + + PTF_ASSERT_EQUAL(gvcpRequestLayer->getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getFlag(), 0x01); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getCommand(), GvcpCommand::ForceIpCmd); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getDataSize(), gvcpRequestLayer->getLayerPayloadSize()); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getRequestId(), 8787); + + PTF_ASSERT_EQUAL(gvcpRequestLayer->getMacAddress(), pcpp::MacAddress("8c:e9:b4:01:63:b2")); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getIpAddress(), pcpp::IPv4Address("192.168.5.1")); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getSubnetMask(), pcpp::IPv4Address("255.255.0.0")); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getGatewayIpAddress(), pcpp::IPv4Address("0.0.0.0")); + } +} + +PTF_TEST_CASE(GvcpForceIpAck) +{ + // test the creation from the raw buffer + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_forceip_ack.dat"); + pcpp::Packet forceIpAckPacket(&rawPacket1); + + auto udpLayer = forceIpAckPacket.getLayerOfType(); + + // we get the raw buffer from the payload of the UDP layer and create a GvcpAcknowledgeLayer from the buffer + GvcpForceIpAcknowledgeLayer gvcpAcknowledgeLayer(udpLayer->getLayerPayload(), udpLayer->getLayerPayloadSize()); + + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getStatus(), GvcpResponseStatus::Success); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getCommand(), GvcpCommand::ForceIpAck); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getAckId(), 8787); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getDataSize(), 0); + } + + // test the GVCP layer directly from the packet + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_forceip_ack.dat"); + pcpp::Packet forceIpAckPacket(&rawPacket1); + + // we get the GVCP layer from the packet + auto gvcpAcknowledgeLayer = forceIpAckPacket.getLayerOfType(); + + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getStatus(), GvcpResponseStatus::Success); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getCommand(), GvcpCommand::ForceIpAck); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getAckId(), 8787); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getDataSize(), 0); + } +} + +PTF_TEST_CASE(GvcpReadRegisterCommand) +{ + // test the creation from the raw buffer + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_readreg_cmd.dat"); + pcpp::Packet readRegCmdPacket(&rawPacket1); + + auto udpLayer = readRegCmdPacket.getLayerOfType(); + + // we get the raw buffer from the payload of the UDP layer and create a GvcpRequestLayer from the buffer + GvcpRequestLayer gvcpRequestLayer(udpLayer->getLayerPayload(), udpLayer->getLayerPayloadSize()); + + PTF_ASSERT_EQUAL(gvcpRequestLayer.getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getFlag(), 0x01); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getCommand(), GvcpCommand::ReadRegCmd); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getDataSize(), + udpLayer->getLayerPayloadSize() - sizeof(internal::gvcp_request_header)); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getRequestId(), 35824); + + auto payload = gvcpRequestLayer.getLayerPayload(); + PTF_ASSERT_TRUE(payload != nullptr); + PTF_ASSERT_EQUAL(reinterpret_cast(payload)[0], 0x00000000); + } + + // test the GVCP layer directly from the packet + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_readreg_cmd.dat"); + pcpp::Packet readRegCmdPacket(&rawPacket1); + + // we get the GVCP layer from the packet + auto gvcpRequestLayer = readRegCmdPacket.getLayerOfType(); + + PTF_ASSERT_EQUAL(gvcpRequestLayer->getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getFlag(), 0x01); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getCommand(), GvcpCommand::ReadRegCmd); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getDataSize(), gvcpRequestLayer->getLayerPayloadSize()); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getRequestId(), 35824); + + auto payload = gvcpRequestLayer->getLayerPayload(); + PTF_ASSERT_TRUE(payload != nullptr); + PTF_ASSERT_EQUAL(reinterpret_cast(payload)[0], 0x00000000); + } +} + +PTF_TEST_CASE(GvcpReadRegisterAcknowledge) +{ + // test the creation from the raw buffer + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_readreg_ack.dat"); + pcpp::Packet readRegAckPacket(&rawPacket1); + + auto udpLayer = readRegAckPacket.getLayerOfType(); + + // we get the raw buffer from the payload of the UDP layer and create a GvcpAcknowledgeLayer from the buffer + GvcpAcknowledgeLayer gvcpAcknowledgeLayer(udpLayer->getLayerPayload(), udpLayer->getLayerPayloadSize()); + + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getAckId(), 0x1fee); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getCommand(), GvcpCommand::ReadRegAck); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getDataSize(), + udpLayer->getLayerPayloadSize() - sizeof(internal::gvcp_ack_header)); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getStatus(), 0x0000); + + auto payload = gvcpAcknowledgeLayer.getLayerPayload(); + PTF_ASSERT_TRUE(payload != nullptr); + PTF_ASSERT_EQUAL(reinterpret_cast(payload)[0], hostToNet32(0x80000001)); + } + + // test the GVCP layer directly from the packet + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_readreg_ack.dat"); + pcpp::Packet readRegAckPacket(&rawPacket1); + + // we get the GVCP layer from the packet + auto gvcpAcknowledgeLayer = readRegAckPacket.getLayerOfType(); + + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getAckId(), 0x1fee); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getCommand(), GvcpCommand::ReadRegAck); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getDataSize(), gvcpAcknowledgeLayer->getLayerPayloadSize()); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getStatus(), 0x0000); + + auto payload = gvcpAcknowledgeLayer->getLayerPayload(); + PTF_ASSERT_TRUE(payload != nullptr); + PTF_ASSERT_EQUAL(reinterpret_cast(payload)[0], hostToNet32(0x80000001)); + } +} + +PTF_TEST_CASE(GvcpWriteRegisterCommand) +{ + // test the creation from the raw buffer + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_writereg_cmd.dat"); + pcpp::Packet writeRegCmdPacket(&rawPacket1); + + auto udpLayer = writeRegCmdPacket.getLayerOfType(); + + // we get the raw buffer from the payload of the UDP layer and create a GvcpRequestLayer from the buffer + GvcpRequestLayer gvcpRequestLayer(udpLayer->getLayerPayload(), udpLayer->getLayerPayloadSize()); + + PTF_ASSERT_EQUAL(gvcpRequestLayer.getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getFlag(), 0x01); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getCommand(), GvcpCommand::WriteRegCmd); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getDataSize(), + udpLayer->getLayerPayloadSize() - sizeof(internal::gvcp_request_header)); + PTF_ASSERT_EQUAL(gvcpRequestLayer.getRequestId(), 8788); + + auto payload = gvcpRequestLayer.getLayerPayload(); + PTF_ASSERT_TRUE(payload != nullptr); + auto payloadHex = pcpp::byteArrayToHexString(payload, gvcpRequestLayer.getLayerPayloadSize(), -1); + const std::string correctPayload = "00000a00000000020000064cc0a805010000065cffff00000000001400000005"; + PTF_ASSERT_EQUAL(payloadHex, correctPayload); + } + + // test the GVCP layer directly from the packet + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_writereg_cmd.dat"); + pcpp::Packet writeRegCmdPacket(&rawPacket1); + + // we get the GVCP layer from the packet + auto gvcpRequestLayer = writeRegCmdPacket.getLayerOfType(); + + PTF_ASSERT_EQUAL(gvcpRequestLayer->getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getFlag(), 0x01); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getCommand(), GvcpCommand::WriteRegCmd); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getDataSize(), gvcpRequestLayer->getLayerPayloadSize()); + PTF_ASSERT_EQUAL(gvcpRequestLayer->getRequestId(), 8788); + + auto payload = gvcpRequestLayer->getLayerPayload(); + PTF_ASSERT_TRUE(payload != nullptr); + auto payloadHex = pcpp::byteArrayToHexString(payload, gvcpRequestLayer->getLayerPayloadSize(), -1); + const std::string correctPayload = "00000a00000000020000064cc0a805010000065cffff00000000001400000005"; + PTF_ASSERT_EQUAL(payloadHex, correctPayload); + } +} + +PTF_TEST_CASE(GvcpWriteRegisterAcknowledge) +{ + // test the creation from the raw buffer + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_writereg_ack.dat"); + pcpp::Packet writeRegAckPacket(&rawPacket1); + + auto udpLayer = writeRegAckPacket.getLayerOfType(); + + // we get the raw buffer from the payload of the UDP layer and create a GvcpAcknowledgeLayer from the buffer + GvcpAcknowledgeLayer gvcpAcknowledgeLayer(udpLayer->getLayerPayload(), udpLayer->getLayerPayloadSize()); + + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getAckId(), 8788); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getCommand(), GvcpCommand::WriteRegAck); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getStatus(), 0x8006); + // In this file, the header data size is 0, but there are paddings in the payload, so we don't compare with the + // UDP's payload size. + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer.getDataSize(), 0); + } + + // test the GVCP layer directly from the packet + { + using namespace pcpp; + + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/gvcp_writereg_ack.dat"); + pcpp::Packet writeRegAckPacket(&rawPacket1); + + // we get the GVCP layer from the packet + auto gvcpAcknowledgeLayer = writeRegAckPacket.getLayerOfType(); + + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getProtocol(), GVCP); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getAckId(), 8788); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getCommand(), GvcpCommand::WriteRegAck); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getDataSize(), gvcpAcknowledgeLayer->getLayerPayloadSize()); + PTF_ASSERT_EQUAL(gvcpAcknowledgeLayer->getStatus(), 0x8006); + } +} diff --git a/Tests/Packet++Test/main.cpp b/Tests/Packet++Test/main.cpp index 4163780110..ea74baa018 100644 --- a/Tests/Packet++Test/main.cpp +++ b/Tests/Packet++Test/main.cpp @@ -256,6 +256,16 @@ int main(int argc, char* argv[]) PTF_RUN_TEST(GtpV2LayerCreationTest, "gtp"); PTF_RUN_TEST(GtpV2LayerEditTest, "gtp"); + PTF_RUN_TEST(GvcpBasicTest, "gvcp"); + PTF_RUN_TEST(GvcpDiscoveryAck, "gvcp"); + PTF_RUN_TEST(GvcpForceIpCommand, "gvcp"); + PTF_RUN_TEST(GvcpDiscoveryCommand, "gvcp"); + PTF_RUN_TEST(GvcpForceIpAck, "gvcp"); + PTF_RUN_TEST(GvcpReadRegisterCommand, "gvcp"); + PTF_RUN_TEST(GvcpReadRegisterAcknowledge, "gvcp"); + PTF_RUN_TEST(GvcpWriteRegisterCommand, "gvcp"); + PTF_RUN_TEST(GvcpWriteRegisterAcknowledge, "gvcp"); + PTF_RUN_TEST(BgpLayerParsingTest, "bgp"); PTF_RUN_TEST(BgpLayerCreationTest, "bgp"); PTF_RUN_TEST(BgpLayerEditTest, "bgp");