diff --git a/Packet++/CMakeLists.txt b/Packet++/CMakeLists.txt index bab9488be..b12596c39 100644 --- a/Packet++/CMakeLists.txt +++ b/Packet++/CMakeLists.txt @@ -19,6 +19,7 @@ add_library( src/IcmpLayer.cpp src/IcmpV6Layer.cpp src/IgmpLayer.cpp + src/InfiniBandLayer.cpp src/IPReassembly.cpp src/IPSecLayer.cpp src/IPv4Layer.cpp @@ -88,6 +89,7 @@ set(public_headers header/IcmpLayer.h header/IcmpV6Layer.h header/IgmpLayer.h + header/InfiniBandLayer.h header/IPLayer.h header/IPReassembly.h header/IPSecLayer.h diff --git a/Packet++/header/InfiniBandLayer.h b/Packet++/header/InfiniBandLayer.h new file mode 100644 index 000000000..376b7c3d9 --- /dev/null +++ b/Packet++/header/InfiniBandLayer.h @@ -0,0 +1,255 @@ +#pragma once + +#include "Layer.h" + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + /** + * @class InfiniBandLayer + * Represents an InfiniBand protocol layer + */ + class InfiniBandLayer : public Layer + { + private: + /** + * @struct bth + * Represents an Base Transport Header + */ +#pragma pack(push, 1) + struct rxe_bth + { + uint8_t opcode; + uint8_t flags; + uint16_t pkey; + uint32_t qpn; + uint32_t apsn; + }; +#pragma pack(pop) + + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to bth_header) + * @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 + */ + InfiniBandLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet, InfiniBand) + {} + + /** + * A constructor that creates a new rxe_bth header and allocates the data + * @param[in] opcode The operation code + * @param[in] soliciteEvent The solicited event + * @param[in] migrationState The migration state + * @param[in] padCount The pad count + * @param[in] partitionKey The partition key + * @param[in] queuePairNumber The destination queue pair (QP) number + * @param[in] ackReq The acknowledgment request + * @param[in] packetSequenceNumber The packet sequence number + */ + InfiniBandLayer(uint8_t opcode, int soliciteEvent, int migrationState, int padCount, uint16_t partitionKey, + uint32_t queuePairNumber, int ackReq, uint32_t packetSequenceNumber); + + /** + * @return The operation code which defines the interpretation of the remaining header and payload bytes + */ + uint8_t getOpcode() const; + + /** + * Set operation code + * @param[in] opcode The opcode to set + */ + void setOpcode(uint8_t opcode) const; + + /** + * @return solicited event that the responder shall invoke the CQ event handler + */ + bool getSoliciteEvent() const; + + /** + * Set solicited event + * @param[in] se The solicited event to set + */ + void setSolicitedEvent(bool se) const; + + /** + * @return migreq which used to communicate migration state + */ + bool getMigrationState() const; + + /** + * Set migreq + * @param[in] mig The migration state to set. If set to one, indicates the connection or EE context has been + * migrated; if set to zero, it means there is no change in the current migration state. + */ + void setMigrationState(bool mig) const; + + /** + * @return PadCount which Packet payloads are sent as a multiple of 4-byte quantities. + * Pad count indicates the number of pad bytes - 0 to 3 - that are appended to the packetpayload. + * Pads are used to “stretch” the payload (payloads may be zero or more bytes in length) to be a multiple of 4 + * bytes + */ + uint8_t getPadCount() const; + + /** + * Set PadCount + * @param[in] pad The PadCount to set + */ + void setPadCount(uint8_t pad) const; + + /** + * @return Transport Header Version that specifies the version of the IBA Transport used for this packet + */ + uint8_t getTransportHeaderVersion() const; + + /** + * Set Transport Header Version + * @param[in] tvr The transport header version to set + */ + void setTransportHeaderVersion(uint8_t tver) const; + + /** + * @return partition key identifying the partition + * that the destination QP (RC, UC, UD, XRC) or EE Context (RD) is a member. + */ + uint16_t getPartitionKey() const; + + /** + * Set partition key + * @param[in] pkey The partition key to set + */ + void setPartitionKey(uint16_t pkey) const; + + /** + * @return destination queue pair (QP) identifier + */ + uint32_t getQueuePairNumber() const; + + /** + * Set Queue Pair Number + * @param[in] qpn The queue pair number to set + */ + void setQueuePairNumber(uint32_t qpn) const; + + /** + * @return FECN + * F (FECN): false indicates that a FECN indication was not received. + * true indicates that the packet went through a point of congestion + */ + bool getFecn() const; + + /** + * Set Fecn + * @param[in] fecn The FECN to set + */ + void setFecn(bool fecn) const; + + /** + * @return BECN + * B (BECN): false the packet did not go through a point of congestion or went + * through a point of congestion but was not marked. true indicates that the + * packet indicated by this header was subject to forward congestion. The B + * bit is set in an ACK or CN BTH + */ + bool getBecn() const; + + /** + * Set BECN + * @param[in] becn The BECN to set + */ + void setBecn(bool becn) const; + + /** + * Set Reserved 6 bits + */ + void setResv6a() const; + + /** + * @return ackreq that requests responder to schedule an acknowledgment on the associated QP. + */ + int getAck() const; + + /** + * Set acknowledgment for requests + * @param[in] ack The acknowledgment to set + */ + void setAck(int ack) const; + + /** + * @return packet sequence number that is used to identify the position of a packet + * within a sequence of packets. + */ + uint32_t getPacketSequenceNumber() const; + + /** + * Set packet sequence number + * @param[in] psn The packet sequence number to set + */ + void setPacketSequenceNumber(uint32_t psn) const; + + /** + * Identify the next layer as PayloadLayer + */ + void parseNextLayer() override; + + /** + * @return Size of rxe_bth header + */ + size_t getHeaderLen() const override + { + return sizeof(rxe_bth); + } + + /** + * Does nothing for this layer + */ + void computeCalculateFields() override + {} + + std::string toString() const override; + + OsiModelLayer getOsiModelLayer() const override + { + return OsiModelTransportLayer; + } + + /** + * A static method that check whether is inifiniband RoCE port + * @param[in] port The port from UDP destination port + * @return True if the port is inifiniband RoCE and can represent an rxe packet + */ + static inline bool isInfiniBandPort(uint16_t port) + { + return (port == 4791); + } + + /** + * The static method makes validation of UDP data + * @param[in] udpData The pointer to the UDP payload data. It points to the first byte of rxe_bth header. + * @param[in] udpDataLen The payload data size + * @return True if the data is valid and can represent the rxe_bth packet + */ + static bool isDataValid(const uint8_t* udpData, size_t udpDataLen); + + private: + /** + * Get a pointer to the BTH header. Notice this points directly to the data, so every change will change + * the actual packet data + * @return A pointer to the bth_header + */ + rxe_bth* getBthHeader() const + { + return reinterpret_cast(m_Data); + } + }; + +} // namespace pcpp diff --git a/Packet++/header/ProtocolType.h b/Packet++/header/ProtocolType.h index f853b63f8..2c0e32637 100644 --- a/Packet++/header/ProtocolType.h +++ b/Packet++/header/ProtocolType.h @@ -352,6 +352,11 @@ namespace pcpp */ const ProtocolType WireGuard = 56; + /* + * InfiniBand protocol + */ + const ProtocolType InfiniBand = 57; + /** * GTPv2 protocol */ diff --git a/Packet++/src/InfiniBandLayer.cpp b/Packet++/src/InfiniBandLayer.cpp new file mode 100644 index 000000000..f527aa2d3 --- /dev/null +++ b/Packet++/src/InfiniBandLayer.cpp @@ -0,0 +1,212 @@ +#include "InfiniBandLayer.h" +#include "PayloadLayer.h" +#include "PacketUtils.h" +#include "Logger.h" +#include +#include "EndianPortable.h" + +namespace pcpp +{ +#define RXE_ICRC_SIZE 4 +#define RXE_MAX_HDR_LENGTH 80 + +#define BTH_TVER 0x0 +#define BTH_DEF_PKEY 0xffff + +#define BTH_SE_MASK 0x80 +#define BTH_MIG_MASK 0x40 +#define BTH_PAD_MASK 0x30 +#define BTH_TVER_MASK 0x0f +#define BTH_FECN_MASK 0x80000000 +#define BTH_BECN_MASK 0x40000000 +#define BTH_RESV6A_MASK 0x3f000000 +#define BTH_QPN_MASK 0x00ffffff +#define BTH_ACK_MASK 0x80000000 +#define BTH_RESV7_MASK 0x7f000000 +#define BTH_PSN_MASK 0x00ffffff + + InfiniBandLayer::InfiniBandLayer(uint8_t opcode, int soliciteEvent, int migrationState, int padCount, uint16_t partitionKey, + uint32_t queuePairNumber, int ackReq, uint32_t packetSequenceNumber) + { + const size_t headerLen = sizeof(rxe_bth); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + rxe_bth* bthHdr = reinterpret_cast(m_Data); + + bthHdr->opcode = opcode; + bthHdr->flags = (padCount << 4) & BTH_PAD_MASK; + if (soliciteEvent) + bthHdr->flags |= BTH_SE_MASK; + if (migrationState) + bthHdr->flags |= BTH_MIG_MASK; + bthHdr->pkey = htobe16(partitionKey); + bthHdr->qpn = htobe32(queuePairNumber & BTH_QPN_MASK); + packetSequenceNumber &= BTH_PSN_MASK; + if (ackReq) + packetSequenceNumber |= BTH_ACK_MASK; + bthHdr->apsn = htobe32(packetSequenceNumber); + m_Protocol = InfiniBand; + } + + void InfiniBandLayer::parseNextLayer() + { + if (m_DataLen <= sizeof(rxe_bth)) + return; + + uint8_t* bthData = m_Data + sizeof(rxe_bth); + size_t bthDataLen = m_DataLen - sizeof(rxe_bth); + + m_NextLayer = new PayloadLayer(bthData, bthDataLen, this, m_Packet); + } + + std::string InfiniBandLayer::toString() const + { + std::ostringstream ss; + ss << "InfiniBand Layer, Opcode: " << static_cast(getOpcode()); + return ss.str(); + } + + uint8_t InfiniBandLayer::getOpcode() const + { + return getBthHeader()->opcode; + } + + void InfiniBandLayer::setOpcode(uint8_t opcode) const + { + getBthHeader()->opcode = opcode; + } + + bool InfiniBandLayer::getSoliciteEvent() const + { + return 0 != (BTH_SE_MASK & getBthHeader()->flags); + } + + void InfiniBandLayer::setSolicitedEvent(bool se) const + { + if (se) + getBthHeader()->flags |= BTH_SE_MASK; + else + getBthHeader()->flags &= ~BTH_SE_MASK; + } + + bool InfiniBandLayer::getMigrationState() const + { + return 0 != (BTH_MIG_MASK & getBthHeader()->flags); + } + + void InfiniBandLayer::setMigrationState(bool mig) const + { + if (mig) + getBthHeader()->flags |= BTH_MIG_MASK; + else + getBthHeader()->flags &= ~BTH_MIG_MASK; + } + + uint8_t InfiniBandLayer::getPadCount() const + { + return (BTH_PAD_MASK & getBthHeader()->flags) >> 4; + } + + void InfiniBandLayer::setPadCount(uint8_t pad) const + { + getBthHeader()->flags = (BTH_PAD_MASK & (pad << 4)) | (~BTH_PAD_MASK & getBthHeader()->flags); + } + + uint8_t InfiniBandLayer::getTransportHeaderVersion() const + { + return BTH_TVER_MASK & getBthHeader()->flags; + } + + void InfiniBandLayer::setTransportHeaderVersion(uint8_t tver) const + { + getBthHeader()->flags = (BTH_TVER_MASK & tver) | (~BTH_TVER_MASK & getBthHeader()->flags); + } + + uint16_t InfiniBandLayer::getPartitionKey() const + { + return be16toh(getBthHeader()->pkey); + } + + void InfiniBandLayer::setPartitionKey(uint16_t pkey) const + { + getBthHeader()->pkey = htobe16(pkey); + } + + uint32_t InfiniBandLayer::getQueuePairNumber() const + { + return BTH_QPN_MASK & be32toh(getBthHeader()->qpn); + } + + void InfiniBandLayer::setQueuePairNumber(uint32_t qpn) const + { + uint32_t resvqpn = be32toh(getBthHeader()->qpn); + + getBthHeader()->qpn = htobe32((BTH_QPN_MASK & qpn) | (~BTH_QPN_MASK & resvqpn)); + } + + bool InfiniBandLayer::getFecn() const + { + return 0 != (htobe32(BTH_FECN_MASK) & getBthHeader()->qpn); + } + + void InfiniBandLayer::setFecn(bool fecn) const + { + if (fecn) + getBthHeader()->qpn |= htobe32(BTH_FECN_MASK); + else + getBthHeader()->qpn &= ~htobe32(BTH_FECN_MASK); + } + + bool InfiniBandLayer::getBecn() const + { + return 0 != (htobe32(BTH_BECN_MASK) & getBthHeader()->qpn); + } + + void InfiniBandLayer::setBecn(bool becn) const + { + if (becn) + getBthHeader()->qpn |= htobe32(BTH_BECN_MASK); + else + getBthHeader()->qpn &= ~htobe32(BTH_BECN_MASK); + } + + void InfiniBandLayer::setResv6a() const + { + getBthHeader()->qpn = htobe32(~BTH_RESV6A_MASK); + } + + int InfiniBandLayer::getAck() const + { + return 0 != (htobe32(BTH_ACK_MASK) & getBthHeader()->apsn); + } + + void InfiniBandLayer::setAck(int ack) const + { + if (ack) + getBthHeader()->apsn |= htobe32(BTH_ACK_MASK); + else + getBthHeader()->apsn &= ~htobe32(BTH_ACK_MASK); + } + + uint32_t InfiniBandLayer::getPacketSequenceNumber() const + { + return BTH_PSN_MASK & be32toh(getBthHeader()->apsn); + } + + void InfiniBandLayer::setPacketSequenceNumber(uint32_t psn) const + { + uint32_t apsn = be32toh(getBthHeader()->apsn); + + getBthHeader()->apsn = htobe32((BTH_PSN_MASK & psn) | (~BTH_PSN_MASK & apsn)); + } + + bool InfiniBandLayer::isDataValid(const uint8_t* udpData, size_t udpDataLen) + { + if (udpData != nullptr && udpDataLen >= sizeof(rxe_bth)) + { + return true; + } + return false; + } +} // namespace pcpp diff --git a/Packet++/src/UdpLayer.cpp b/Packet++/src/UdpLayer.cpp index 9cfaa2cbe..2e82a72ab 100644 --- a/Packet++/src/UdpLayer.cpp +++ b/Packet++/src/UdpLayer.cpp @@ -5,6 +5,7 @@ #include "PayloadLayer.h" #include "IPv4Layer.h" #include "IPv6Layer.h" +#include "InfiniBandLayer.h" #include "DnsLayer.h" #include "DhcpLayer.h" #include "DhcpV6Layer.h" @@ -144,6 +145,9 @@ namespace pcpp if (!m_NextLayer) m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); } + else if (InfiniBandLayer::isInfiniBandPort(portDst) && + InfiniBandLayer::isDataValid(udpData, udpDataLen)) + m_NextLayer = new InfiniBandLayer(udpData, udpDataLen, this, m_Packet); else m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); } diff --git a/Tests/Packet++Test/CMakeLists.txt b/Tests/Packet++Test/CMakeLists.txt index 0bcac69e6..514b0cbb1 100644 --- a/Tests/Packet++Test/CMakeLists.txt +++ b/Tests/Packet++Test/CMakeLists.txt @@ -15,6 +15,7 @@ add_executable( Tests/IcmpTests.cpp Tests/IcmpV6Tests.cpp Tests/IgmpTests.cpp + Tests/InfiniBandTests.cpp Tests/IPSecTests.cpp Tests/IPv4Tests.cpp Tests/IPv6Tests.cpp diff --git a/Tests/Packet++Test/PacketExamples/InfiniBandPacket.dat b/Tests/Packet++Test/PacketExamples/InfiniBandPacket.dat new file mode 100644 index 000000000..7c58b77e1 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/InfiniBandPacket.dat @@ -0,0 +1 @@ +000c29ae1ca4000c2989a2e508004500003cbe10400040118a4bc0a83881c0a83883df9412b7002800000c00ffff000000118054cb6300007fee260fb000000002b80001000008c6154a \ No newline at end of file diff --git a/Tests/Packet++Test/TestDefinition.h b/Tests/Packet++Test/TestDefinition.h index 9047e2682..408ba889d 100644 --- a/Tests/Packet++Test/TestDefinition.h +++ b/Tests/Packet++Test/TestDefinition.h @@ -274,3 +274,7 @@ PTF_TEST_CASE(WireGuardCookieReplyParsingTest); PTF_TEST_CASE(WireGuardTransportDataParsingTest); PTF_TEST_CASE(WireGuardCreationTest); PTF_TEST_CASE(WireGuardEditTest); + +// Implemented in InfiniBandTests.cpp +PTF_TEST_CASE(InfiniBandParsingTest); +PTF_TEST_CASE(InfiniBandCreationTest); diff --git a/Tests/Packet++Test/Tests/InfiniBandTests.cpp b/Tests/Packet++Test/Tests/InfiniBandTests.cpp new file mode 100644 index 000000000..9e2506e55 --- /dev/null +++ b/Tests/Packet++Test/Tests/InfiniBandTests.cpp @@ -0,0 +1,144 @@ +#include "../TestDefinition.h" +#include "../Utils/TestUtils.h" +#include +#include "EndianPortable.h" +#include "Logger.h" +#include "MacAddress.h" +#include "Packet.h" +#include "EthLayer.h" +#include "VlanLayer.h" +#include "IPv4Layer.h" +#include "IPv6Layer.h" +#include "UdpLayer.h" +#include "InfiniBandLayer.h" +#include "PayloadLayer.h" +#include "SystemUtils.h" + +std::string getProtocolTypeAsString(pcpp::ProtocolType protocolType) +{ + switch (protocolType) + { + case pcpp::Ethernet: + return "Ethernet"; + case pcpp::VLAN: + return "Vlan"; + case pcpp::IPv4: + return "IPv4"; + case pcpp::UDP: + return "UDP"; + case pcpp::TCP: + return "TCP"; + case pcpp::InfiniBand: + return "InfiniBand"; + case pcpp::HTTPRequest: + case pcpp::HTTPResponse: + return "HTTP"; + case pcpp::GenericPayload: + return "Payload"; + default: + return "Unknown"; + } +} + +PTF_TEST_CASE(InfiniBandParsingTest) +{ + timeval time; + gettimeofday(&time, nullptr); + + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/InfiniBandPacket.dat"); + + pcpp::Packet ip4Packet(&rawPacket1); + + // parse the raw packet into a parsed packet + pcpp::Packet parsedPacket(&rawPacket1); + + // first let's go over the layers one by one and find out its type, its total length, + // its header length and its payload length + for (auto* curLayer = parsedPacket.getFirstLayer(); curLayer != nullptr; curLayer = curLayer->getNextLayer()) + { + switch (curLayer->getProtocol()) + { + case pcpp::Ethernet: + { + // now let's get the Ethernet layer + auto* ethernetLayer = parsedPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(ethernetLayer); + break; + } + case pcpp::VLAN: + { + // now let's get the Vlan layer + auto* vlanLayer = parsedPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(vlanLayer); + break; + } + case pcpp::IPv4: + { + // let's get the IPv4 layer + auto* ipLayer = parsedPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(ipLayer); + break; + } + case pcpp::UDP: + { + // let's get the UDP layer + auto* udpLayer = parsedPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(udpLayer); + PTF_ASSERT_EQUAL(udpLayer->getSrcPort(), 57236); + PTF_ASSERT_EQUAL(udpLayer->getDstPort(), 4791); + break; + } + case pcpp::InfiniBand: + { + // let's get the InfiniBand layer + auto* ibLayer = parsedPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(ibLayer); + PTF_ASSERT_EQUAL(ibLayer->getOpcode(), 12); + PTF_ASSERT_EQUAL(ibLayer->getSoliciteEvent(), false); + PTF_ASSERT_EQUAL(ibLayer->getMigrationState(), false); + PTF_ASSERT_EQUAL(ibLayer->getPadCount(), 0); + PTF_ASSERT_EQUAL(ibLayer->getTransportHeaderVersion(), 0); + PTF_ASSERT_EQUAL(ibLayer->getPartitionKey(), 65535); + PTF_ASSERT_EQUAL(ibLayer->getQueuePairNumber(), 17); + PTF_ASSERT_EQUAL(ibLayer->getFecn(), false); + PTF_ASSERT_EQUAL(ibLayer->getBecn(), false); + PTF_ASSERT_EQUAL(ibLayer->getAck(), 1); + PTF_ASSERT_EQUAL(ibLayer->getPacketSequenceNumber(), 5557091); + break; + } + case pcpp::GenericPayload: + { + break; + } + default: + { + break; + } + } + } +} // InfiniBandParsingTest + +PTF_TEST_CASE(InfiniBandCreationTest) +{ + pcpp::EthLayer ethLayer1(pcpp::MacAddress("02:7d:fa:01:17:40"), pcpp::MacAddress("02:7d:fa:00:10:01"), + PCPP_ETHERTYPE_IP); + pcpp::IPv4Layer ipLayer1(pcpp::IPv4Address("192.168.0.1"), pcpp::IPv4Address("192.168.0.2")); + pcpp::UdpLayer udpLayer1(30502, 4791); + pcpp::InfiniBandLayer infinibandLayer1(12, 0, 0, 0, 65535, 17, 1, 5557091); + + pcpp::Packet infinibandPacket1(100); + PTF_ASSERT_TRUE(infinibandPacket1.addLayer(ðLayer1)); + PTF_ASSERT_TRUE(infinibandPacket1.addLayer(&ipLayer1)); + PTF_ASSERT_TRUE(infinibandPacket1.addLayer(&udpLayer1)); + PTF_ASSERT_TRUE(infinibandPacket1.addLayer(&infinibandLayer1)); + + PTF_ASSERT_EQUAL(infinibandPacket1.getLayerOfType()->getDataLen(), 20); + PTF_ASSERT_NOT_NULL(infinibandPacket1.getLayerOfType()); + PTF_ASSERT_EQUAL(udpLayer1.getDstPort(), 4791); + PTF_ASSERT_EQUAL(infinibandLayer1.getOpcode(), 12); + PTF_ASSERT_EQUAL(infinibandLayer1.getPartitionKey(), 65535); + PTF_ASSERT_EQUAL(infinibandLayer1.getQueuePairNumber(), 17); + PTF_ASSERT_EQUAL(infinibandLayer1.getAck(), 1); + PTF_ASSERT_EQUAL(infinibandLayer1.getPacketSequenceNumber(), 5557091); + +} // InfiniBandCreationTest diff --git a/Tests/Packet++Test/main.cpp b/Tests/Packet++Test/main.cpp index 416378011..42f4e29ef 100644 --- a/Tests/Packet++Test/main.cpp +++ b/Tests/Packet++Test/main.cpp @@ -342,5 +342,8 @@ int main(int argc, char* argv[]) PTF_RUN_TEST(WireGuardCreationTest, "wg"); PTF_RUN_TEST(WireGuardEditTest, "wg"); + PTF_RUN_TEST(InfiniBandParsingTest, "ib"); + PTF_RUN_TEST(InfiniBandCreationTest, "ib"); + PTF_END_RUNNING_TESTS; }