diff --git a/Packet++/CMakeLists.txt b/Packet++/CMakeLists.txt index fc3fd96078..533c64dcc1 100644 --- a/Packet++/CMakeLists.txt +++ b/Packet++/CMakeLists.txt @@ -37,6 +37,7 @@ add_library( src/PPPoELayer.cpp src/RadiusLayer.cpp src/RawPacket.cpp + src/S7CommLayer.cpp src/SdpLayer.cpp src/SingleCommandTextProtocol.cpp src/SipLayer.cpp @@ -103,6 +104,7 @@ set(public_headers header/ProtocolType.h header/RadiusLayer.h header/RawPacket.h + header/S7CommLayer.h header/SdpLayer.h header/SingleCommandTextProtocol.h header/SipLayer.h diff --git a/Packet++/header/CotpLayer.h b/Packet++/header/CotpLayer.h index 48e933f659..119737a28e 100644 --- a/Packet++/header/CotpLayer.h +++ b/Packet++/header/CotpLayer.h @@ -95,7 +95,7 @@ namespace pcpp void computeCalculateFields() override {} /** - * Currently parses the rest of the packet as a generic payload (PayloadLayer) + * Currently parses the rest of the packet as a S7COMM or generic payload (PayloadLayer) */ void parseNextLayer() override; diff --git a/Packet++/header/ProtocolType.h b/Packet++/header/ProtocolType.h index 883aec32c1..31dc821ed2 100644 --- a/Packet++/header/ProtocolType.h +++ b/Packet++/header/ProtocolType.h @@ -327,6 +327,11 @@ namespace pcpp */ const ProtocolType SLL2 = 0x8000000000000; + /** + * S7COMM protocol + */ + const ProtocolType S7COMM = 0x10000000000000; + /** * An enum representing OSI model layers */ diff --git a/Packet++/header/S7CommLayer.h b/Packet++/header/S7CommLayer.h new file mode 100644 index 0000000000..e5cdfbb3f5 --- /dev/null +++ b/Packet++/header/S7CommLayer.h @@ -0,0 +1,220 @@ +#ifndef PACKETPP_S7COMM_LAYER +#define PACKETPP_S7COMM_LAYER + +#include "EthLayer.h" +#include "Layer.h" + +namespace pcpp +{ +/** + * @struct s7commhdr + * Represents a S7COMM protocol header + */ +#pragma pack(push, 1) + typedef struct + { + /** protocol id */ + uint8_t protocolId; + /** message type */ + uint8_t msgType; + /** redundancy identification (reserved) */ + uint16_t reserved; + /** protocol data unit reference */ + uint16_t pduRef; + /** parameter length */ + uint16_t paramLength; + /** data length */ + uint16_t dataLength; + } s7commhdr; +#pragma pack(pop) + +/** + * @struct s7comm_ack_data_hdr + * Represents a S7COMM protocol header with Ack-Data header + */ +#pragma pack(push, 1) + struct s7comm_ack_data_hdr : s7commhdr + { + /** error class */ + uint8_t errorClass; + /** error code */ + uint8_t errorCode; + }; +#pragma pack(pop) + + /** + * @class S7CommParameter + * Represents a S7COMM (S7 Communication) protocol Parameter + */ + class S7CommParameter + { + friend class S7CommLayer; + + public: + S7CommParameter() {} + + virtual ~S7CommParameter() {} + + /** + * @return The data of the Parameter + */ + uint8_t *getData() const { return m_Data; } + /** + * @return The length of the Parameter data + */ + size_t getDataLength() const { return m_DataLen; } + + private: + S7CommParameter(uint8_t *data, size_t dataLen) : m_Data(data), m_DataLen(dataLen) {} + uint8_t *m_Data; + size_t m_DataLen; + }; + /** + * @class S7CommLayer + * Represents a S7COMM (S7 Communication) protocol + */ + class S7CommLayer : public Layer + { + public: + /** + * A constructor that allocates a new S7comm header + * @param[in] msgType The general type of the message + * @param[in] pduRef Link responses to their requests + * @param[in] paramLength The length of the parameter field + * @param[in] dataLength The length of the data field + * @param[in] errorClass The value of the error class + * @param[in] errorCode The value of the error code + */ + S7CommLayer(uint8_t msgType, uint16_t pduRef, uint16_t paramLength, uint16_t dataLength, uint8_t errorClass = 0, + uint8_t errorCode = 0); + + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data (will be casted to @ref s7commhdr) + * @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 + */ + S7CommLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) + : Layer(data, dataLen, prevLayer, packet) + { + m_Protocol = S7COMM; + m_Parameter = nullptr; + } + + virtual ~S7CommLayer() + { + if (m_Parameter) + delete m_Parameter; + } + + /** + * @return S7comm protocol id + */ + uint8_t getProtocolId() const; + + /** + * @return S7comm message type + */ + uint8_t getMsgType() const; + + /** + * @return S7comm PDU ref + */ + uint16_t getPduRef() const; + + /** + * @return S7comm parameter length + */ + uint16_t getParamLength() const; + + /** + * @return S7comm data length + */ + uint16_t getDataLength() const; + + /** + * @return S7comm error code + */ + uint8_t getErrorCode() const; + + /** + * @return S7comm error class + */ + uint8_t getErrorClass() const; + + /** + * @return S7comm parameter + */ + const S7CommParameter *getParameter(); + + /** + * Set the value of the message type + * @param[in] msgType The value of the message type + */ + void setMsgType(uint8_t msgType) const; + + /** + * Set the value of the PDU ref + * @param[in] pduRef The value of the PDU ref + */ + void setPduRef(uint16_t pduRef) const; + + /** + * Set the value of the error code + * @param[in] errorCode The value of the error code + */ + void setErrorCode(uint8_t errorCode) const; + /** + * Set the value of the error class + * @param[in] errorClass The value of the error class + */ + void setErrorClass(uint8_t errorClass) const; + + /** + * @return Size of S7CommLayer + */ + size_t getHeaderLen() const override { return m_DataLen; } + + /** + * Does nothing for this layer (S7CommLayer is always last) + */ + void computeCalculateFields() override {} + + /** + * Does nothing for this layer (S7CommLayer is always last) + */ + void parseNextLayer() override {} + + /** + * A static method that takes a byte array and detects whether it is a S7COMM + * @param[in] data A byte array + * @param[in] dataSize The byte array size (in bytes) + * @return True if the data looks like a valid S7COMM layer + */ + static bool isDataValid(const uint8_t *data, size_t dataSize); + + std::string toString() const override; + + OsiModelLayer getOsiModelLayer() const override { return OsiModelApplicationLayer; } + + private: + s7commhdr *getS7commHeader() const { return (s7commhdr *)m_Data; } + + s7comm_ack_data_hdr *getS7commAckDataHeader() const + { + if (getS7commHeader()->msgType == 0x03) + { + return (s7comm_ack_data_hdr *)m_Data; + } + return nullptr; + } + + size_t getS7commHeaderLength() const; + + S7CommParameter *m_Parameter; + }; + +} // namespace pcpp + +#endif // PACKETPP_S7COMM_LAYER diff --git a/Packet++/src/CotpLayer.cpp b/Packet++/src/CotpLayer.cpp index 546dfe5653..353ebdfabf 100644 --- a/Packet++/src/CotpLayer.cpp +++ b/Packet++/src/CotpLayer.cpp @@ -1,5 +1,6 @@ #include "../header/CotpLayer.h" #include "EndianPortable.h" +#include "S7CommLayer.h" #include #include #include @@ -21,10 +22,7 @@ namespace pcpp m_Protocol = COTP; } - std::string CotpLayer::toString() const - { - return "Cotp Layer"; - } + std::string CotpLayer::toString() const { return "Cotp Layer"; } uint8_t CotpLayer::getLength() const { return getCotpHeader()->length; } @@ -43,7 +41,7 @@ namespace pcpp if (!data || dataSize < sizeof(cotphdr)) return false; - return data[1] == 0xf0 && data[0] == 2; + return data[1] == 0xf0 && data[0] == 2; } void CotpLayer::parseNextLayer() @@ -55,6 +53,9 @@ namespace pcpp uint8_t *payload = m_Data + headerLen; size_t payloadLen = m_DataLen - headerLen; - m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); + if (S7CommLayer::isDataValid(payload, payloadLen)) + m_NextLayer = new S7CommLayer(payload, payloadLen, this, m_Packet); + else + m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet); } } // namespace pcpp diff --git a/Packet++/src/S7CommLayer.cpp b/Packet++/src/S7CommLayer.cpp new file mode 100644 index 0000000000..a61fa1aa28 --- /dev/null +++ b/Packet++/src/S7CommLayer.cpp @@ -0,0 +1,125 @@ +#include "EndianPortable.h" + +#include "S7CommLayer.h" +#include "TcpLayer.h" +#include + +#include +#include + +namespace pcpp +{ + + S7CommLayer::S7CommLayer(uint8_t msgType, uint16_t pduRef, uint16_t paramLength, uint16_t dataLength, + uint8_t errorClass, uint8_t errorCode) + { + size_t basicHeaderLen = msgType == 0x03 ? sizeof(s7comm_ack_data_hdr) : sizeof(s7commhdr); + size_t headerLen = basicHeaderLen + paramLength + dataLength; + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + + if (msgType == 0x03) + { + auto *ack_d = (s7comm_ack_data_hdr *)m_Data; + ack_d->protocolId = 0x32; + ack_d->msgType = msgType; + ack_d->reserved = 0x0000; + ack_d->pduRef = htobe16(pduRef); + ack_d->paramLength = htobe16(paramLength); + ack_d->dataLength = htobe16(dataLength); + ack_d->errorClass = errorClass; + ack_d->errorCode = errorCode; + } + else + { + auto *s7commHdr = (s7commhdr *)m_Data; + s7commHdr->protocolId = 0x32; + s7commHdr->msgType = msgType; + s7commHdr->reserved = 0x0000; + s7commHdr->pduRef = htobe16(pduRef); + s7commHdr->paramLength = htobe16(paramLength); + s7commHdr->dataLength = htobe16(dataLength); + } + + m_Parameter = nullptr; + m_Protocol = S7COMM; + } + + std::string S7CommLayer::toString() const + { + std::ostringstream str; + str << "S7Comm Layer, "; + + switch (getS7commHeader()->msgType) + { + case 0x01: + str << "Job Request"; + break; + case 0x02: + str << "Ack"; + break; + case 0x03: + str << "Ack-Data"; + break; + case 0x07: + str << "Userdata"; + break; + default: + str << "Unknown message"; + } + + return str.str(); + } + + bool S7CommLayer::isDataValid(const uint8_t *data, size_t dataSize) + { + if (!data || dataSize < sizeof(s7commhdr)) + return false; + + return data[0] == 0x32; + } + + uint8_t S7CommLayer::getProtocolId() const { return getS7commHeader()->protocolId; } + + uint8_t S7CommLayer::getMsgType() const { return getS7commHeader()->msgType; } + + uint16_t S7CommLayer::getParamLength() const { return be16toh(getS7commHeader()->paramLength); } + + uint16_t S7CommLayer::getPduRef() const { return be16toh(getS7commHeader()->pduRef); } + + uint16_t S7CommLayer::getDataLength() const { return be16toh(getS7commHeader()->dataLength); } + + void S7CommLayer::setMsgType(uint8_t msgType) const { getS7commHeader()->msgType = msgType; } + + uint8_t S7CommLayer::getErrorCode() const { return getS7commAckDataHeader()->errorCode; } + + uint8_t S7CommLayer::getErrorClass() const { return getS7commAckDataHeader()->errorClass; } + + void S7CommLayer::setPduRef(uint16_t pduRef) const { getS7commHeader()->pduRef = htobe16(pduRef); } + + void S7CommLayer::setErrorCode(uint8_t errorCode) const { getS7commAckDataHeader()->errorCode = errorCode; } + + void S7CommLayer::setErrorClass(uint8_t errorClass) const { getS7commAckDataHeader()->errorClass = errorClass; } + + const S7CommParameter *S7CommLayer::getParameter() + { + if (!m_Parameter) + { + uint8_t *payload = m_Data + getS7commHeaderLength(); + m_Parameter = new S7CommParameter(payload, getParamLength()); + } + + return m_Parameter; + } + + size_t S7CommLayer::getS7commHeaderLength() const + { + if (getS7commHeader()->msgType == 0x03) + { + return sizeof(s7comm_ack_data_hdr); + } + return sizeof(s7commhdr); + } + +} // namespace pcpp diff --git a/README.md b/README.md index 149ddc82ae..7ea0ddb1d4 100644 --- a/README.md +++ b/README.md @@ -250,7 +250,7 @@ PcapPlusPlus currently supports parsing, editing and creation of packets of the ### Application Layer (L7) -34. BGP (v4) +34. BGP (v4) 35. DHCP 36. DHCPv6 37. DNS @@ -258,10 +258,11 @@ PcapPlusPlus currently supports parsing, editing and creation of packets of the 39. HTTP headers (request & response) 40. NTP (v3, v4) 41. Radius -42. SOME/IP -43. SSH - parsing only (no editing capabilities) -44. Telnet - parsing only (no editing capabilities) -45. Generic payload +42. S7 Communication (S7comm) +43. SOME/IP +44. SSH - parsing only (no editing capabilities) +45. Telnet - parsing only (no editing capabilities) +46. Generic payload ## DPDK And PF_RING Support diff --git a/Tests/Packet++Test/CMakeLists.txt b/Tests/Packet++Test/CMakeLists.txt index 3f781b52ac..53c66d0038 100644 --- a/Tests/Packet++Test/CMakeLists.txt +++ b/Tests/Packet++Test/CMakeLists.txt @@ -24,6 +24,7 @@ add_executable( Tests/PacketUtilsTests.cpp Tests/PPPoETests.cpp Tests/RadiusTests.cpp + Tests/S7CommTests.cpp Tests/SipSdpTests.cpp Tests/Sll2Tests.cpp Tests/SllNullLoopbackTests.cpp diff --git a/Tests/Packet++Test/PacketExamples/S7comm.dat b/Tests/Packet++Test/PacketExamples/S7comm.dat new file mode 100644 index 0000000000..5dfdffc949 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/S7comm.dat @@ -0,0 +1 @@ +005056b837d1000e8cd0f9cb0800450001190fc400001e0676970a4900d30a4900200066f0480072171153f0fa9950181000bc050000030000f102f08032070000fd0b000c00d4000112081284010100000000ff0900d000a000000014000a4302ff68c70000000814771421090815333765641381fe64c77200000814771421090815333765544301ff46c7720000081477142109081533375274430eff23000000000000000021073117382880074546ff32c0c08ffe0000000021073117382409174300fff3000000000000000021073117382409176522ff030000000100018ffe21073117382409174580fff200000002000000002107302334535946494eff8fc00000000000000021073023345359264302ff68c7000000001477142104271604011663 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/s7comm.pcap b/Tests/Packet++Test/PacketExamples/s7comm.pcap new file mode 100644 index 0000000000..6c90e2daf6 Binary files /dev/null and b/Tests/Packet++Test/PacketExamples/s7comm.pcap differ diff --git a/Tests/Packet++Test/PacketExamples/s7comm_ack_data.dat b/Tests/Packet++Test/PacketExamples/s7comm_ack_data.dat new file mode 100644 index 0000000000..35697c36d4 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/s7comm_ack_data.dat @@ -0,0 +1 @@ +90e6ba845e41001b1b23eb3b080045000081014900001e0617acc0a80128c0a8010a006610b00002fcf416fe7a2f50181000a73500000300005902f0803203000000000002004400000401ff04020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/s7comm_ack_data.pcap b/Tests/Packet++Test/PacketExamples/s7comm_ack_data.pcap new file mode 100644 index 0000000000..daa4de3ac0 Binary files /dev/null and b/Tests/Packet++Test/PacketExamples/s7comm_ack_data.pcap differ diff --git a/Tests/Packet++Test/TestDefinition.h b/Tests/Packet++Test/TestDefinition.h index d4c1ee8571..69495c7e75 100644 --- a/Tests/Packet++Test/TestDefinition.h +++ b/Tests/Packet++Test/TestDefinition.h @@ -241,5 +241,9 @@ PTF_TEST_CASE(TpktLayerTest); PTF_TEST_CASE(VrrpParsingTest); PTF_TEST_CASE(VrrpCreateAndEditTest); -//Implemented in CotpTests.cpp +// Implemented in CotpTests.cpp PTF_TEST_CASE(CotpLayerTest); + +// Implemented in S7commTests.cpp +PTF_TEST_CASE(S7CommLayerParsingTest); +PTF_TEST_CASE(S7CommLayerCreationTest); diff --git a/Tests/Packet++Test/Tests/S7CommTests.cpp b/Tests/Packet++Test/Tests/S7CommTests.cpp new file mode 100644 index 0000000000..b644ed51fa --- /dev/null +++ b/Tests/Packet++Test/Tests/S7CommTests.cpp @@ -0,0 +1,87 @@ +#include "../TestDefinition.h" +#include "../Utils/TestUtils.h" +#include "EndianPortable.h" +#include "Packet.h" +#include "S7CommLayer.h" +#include "SystemUtils.h" + + +PTF_TEST_CASE(S7CommLayerParsingTest) +{ + timeval time; + gettimeofday(&time, nullptr); + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/S7comm.dat"); + + pcpp::Packet s7CommLayerTest(&rawPacket1); + PTF_ASSERT_TRUE(s7CommLayerTest.isPacketOfType(pcpp::S7COMM)); + auto *S7CommLayer = s7CommLayerTest.getLayerOfType(); + PTF_ASSERT_NOT_NULL(S7CommLayer); + + PTF_ASSERT_EQUAL(S7CommLayer->getProtocolId(), 50); + PTF_ASSERT_EQUAL(S7CommLayer->getMsgType(), 7); + PTF_ASSERT_EQUAL(S7CommLayer->getPduRef(), 64779); + PTF_ASSERT_EQUAL(S7CommLayer->getParamLength(), 12); + PTF_ASSERT_EQUAL(S7CommLayer->getDataLength(), 212); + PTF_ASSERT_EQUAL(S7CommLayer->getHeaderLen(), 234); + PTF_ASSERT_EQUAL(S7CommLayer->toString(), "S7Comm Layer, Userdata"); + + PTF_ASSERT_EQUAL(S7CommLayer->getParameter()->getDataLength(), 12); + uint8_t expectedParameterData[] = {0, 1, 18, 8, 18, 132, 1, 1, 0, 0, 0, 0}; + PTF_ASSERT_BUF_COMPARE(S7CommLayer->getParameter()->getData(), expectedParameterData, 12); + + READ_FILE_AND_CREATE_PACKET(2, "PacketExamples/s7comm_ack_data.dat"); + + pcpp::Packet s7CommLayerTest2(&rawPacket2); + PTF_ASSERT_TRUE(s7CommLayerTest2.isPacketOfType(pcpp::S7COMM)); + auto *s7commLayer = s7CommLayerTest2.getLayerOfType(); + PTF_ASSERT_NOT_NULL(s7commLayer); + PTF_ASSERT_EQUAL(s7commLayer->getProtocolId(), 50); + PTF_ASSERT_EQUAL(s7commLayer->getMsgType(), 3); + PTF_ASSERT_EQUAL(s7commLayer->getPduRef(), 0); + PTF_ASSERT_EQUAL(s7commLayer->getParamLength(), 2); + PTF_ASSERT_EQUAL(s7commLayer->getDataLength(), 68); + PTF_ASSERT_EQUAL(s7commLayer->getErrorClass(), 0); + PTF_ASSERT_EQUAL(s7commLayer->getErrorCode(), 0); + PTF_ASSERT_EQUAL(s7commLayer->getHeaderLen(), 82); + + PTF_ASSERT_EQUAL(s7commLayer->toString(), "S7Comm Layer, Ack-Data"); + + s7commLayer->setErrorCode(6); + s7commLayer->setErrorClass(7); + PTF_ASSERT_EQUAL(s7commLayer->getErrorClass(), 7); + PTF_ASSERT_EQUAL(s7commLayer->getErrorCode(), 6); + PTF_ASSERT_EQUAL(s7commLayer->getParameter()->getDataLength(), 2); + uint8_t expectedErrorParameterData[] = {4, 1}; + PTF_ASSERT_BUF_COMPARE(s7commLayer->getParameter()->getData(), expectedErrorParameterData, 2); +} // S7CommLayerParsingTest + + +PTF_TEST_CASE(S7CommLayerCreationTest) +{ + pcpp::S7CommLayer newS7commLayer(1, 64780, 12, 212); + + PTF_ASSERT_EQUAL(newS7commLayer.getMsgType(), 1); + PTF_ASSERT_EQUAL(newS7commLayer.getPduRef(), 64780); + PTF_ASSERT_EQUAL(newS7commLayer.getParamLength(), 12); + PTF_ASSERT_EQUAL(newS7commLayer.getDataLength(), 212); + PTF_ASSERT_EQUAL(newS7commLayer.getHeaderLen(), 234); + PTF_ASSERT_EQUAL(newS7commLayer.toString(), "S7Comm Layer, Job Request"); + + newS7commLayer.setMsgType(6); + newS7commLayer.setPduRef(64778); + + PTF_ASSERT_EQUAL(newS7commLayer.getMsgType(), 6); + PTF_ASSERT_EQUAL(newS7commLayer.getPduRef(), 64778); + PTF_ASSERT_EQUAL(newS7commLayer.toString(), "S7Comm Layer, Unknown message"); + + newS7commLayer.setMsgType(2); + PTF_ASSERT_EQUAL(newS7commLayer.toString(), "S7Comm Layer, Ack"); + + pcpp::S7CommLayer newS7commLayer2(3, 0, 2, 68, 0x81, 2); + + PTF_ASSERT_EQUAL(newS7commLayer2.getMsgType(), 3); + PTF_ASSERT_EQUAL(newS7commLayer2.getErrorClass(), 0x81); + PTF_ASSERT_EQUAL(newS7commLayer2.getErrorCode(), 2); + PTF_ASSERT_EQUAL(newS7commLayer2.getHeaderLen(), 82); + PTF_ASSERT_EQUAL(newS7commLayer2.toString(), "S7Comm Layer, Ack-Data"); +} // S7CommLayerCreationTest diff --git a/Tests/Packet++Test/main.cpp b/Tests/Packet++Test/main.cpp index 731bf31fdd..d6cca36bf2 100644 --- a/Tests/Packet++Test/main.cpp +++ b/Tests/Packet++Test/main.cpp @@ -316,5 +316,8 @@ int main(int argc, char* argv[]) PTF_RUN_TEST(CotpLayerTest, "cotp"); + PTF_RUN_TEST(S7CommLayerParsingTest, "s7comm"); + PTF_RUN_TEST(S7CommLayerCreationTest, "s7comm"); + PTF_END_RUNNING_TESTS; }