diff --git a/Packet++/CMakeLists.txt b/Packet++/CMakeLists.txt index fc3fd96078..44f2b92b83 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..101870da64 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 = 0x9000000000000; + /** * An enum representing OSI model layers */ diff --git a/Packet++/header/S7commLayer.h b/Packet++/header/S7commLayer.h new file mode 100644 index 0000000000..f156d1f4ab --- /dev/null +++ b/Packet++/header/S7commLayer.h @@ -0,0 +1,149 @@ +#ifndef PCAPPLUSPLUS_S7COMMLAYER_H +#define PCAPPLUSPLUS_S7COMMLAYER_H + +#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 protocol_id; + /** message type */ + uint8_t msg_type; + /** redundancy identification (reserved) */ + uint16_t reserved; + /** protocol data unit reference */ + uint16_t pdu_ref; + /** parameter length */ + uint16_t param_length; + /** data length */ + uint16_t data_length; + } s7commhdr; +#pragma pack(pop) + + /** + * @class S7commLayer + * Represents a S7COMM (S7 Communication7) protocol + */ + class S7commLayer : public Layer + { + public: + /** + * A constructor that allocates a new S7comm header + * @param[in] msg_type The general type of the message + * @param[in] pdu_ref Link responses to their requests + * @param[in] param_length The length of the parameter field + * @param[in] data_length The length of the data field + */ + S7commLayer(uint8_t msg_type, uint16_t pdu_ref, uint16_t param_length, uint16_t data_length); + + /** + * 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; + } + + virtual ~S7commLayer() {} + + /** + * @return S7comm protocol id + */ + uint8_t getProtocolId() const; + + /** + * @return S7comm message type + */ + uint8_t getMsgType() const; + + /** + * @return S7comm reserved + */ + uint16_t getReserved() 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; + + /** + * Set the value of the message type + * @param[in] msg_type The value of the message type + */ + void setMsgType(uint8_t msg_type) const; + + /** + * Set the value of the PDU ref + * @param[in] pdu_ref The value of the PDU ref + */ + void setPduRef(uint16_t pdu_ref) const; + + /** + * Set the value of the parameter length + * @param[in] param_length The value of the parameter length + */ + void setParamLength(uint16_t param_length) const; + + /** + * Set the value of the data length + * @param[in] data_length The value of the data length + */ + void setDataLength(uint16_t data_length) const; + + /** + * @return Size of @ref s7commhdr + */ + size_t getHeaderLen() const override { return sizeof(s7commhdr); } + + /** + * Does nothing for this layer + */ + void computeCalculateFields() override {} + + /** + * Does nothing for this layer + */ + 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; + + OsiModelLayer getOsiModelLayer() const override { return OsiModelApplicationLayer; } + + + private: + s7commhdr *getS7commHeader() const { return (s7commhdr *)m_Data; } + }; + +} // namespace pcpp +#endif // PCAPPLUSPLUS_S7COMMLAYER_H diff --git a/Packet++/src/CotpLayer.cpp b/Packet++/src/CotpLayer.cpp index 546dfe5653..10f0badb17 100644 --- a/Packet++/src/CotpLayer.cpp +++ b/Packet++/src/CotpLayer.cpp @@ -4,6 +4,7 @@ #include #include #include +#include "S7commLayer.h" namespace pcpp { @@ -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..de0f82a9d8 --- /dev/null +++ b/Packet++/src/S7commLayer.cpp @@ -0,0 +1,93 @@ +#include "EndianPortable.h" +#include "TpktLayer.h" + +#include +#include "TcpLayer.h" +#include "S7commLayer.h" + +#include +#include + +namespace pcpp { + + S7commLayer::S7commLayer(uint8_t msg_type, uint16_t pdu_ref, + uint16_t param_length, uint16_t data_length) { + const size_t headerLen = sizeof(s7commhdr); + m_DataLen = headerLen; + m_Data = new uint8_t[headerLen]; + memset(m_Data, 0, headerLen); + s7commhdr *s7commHdr = (s7commhdr *) m_Data; + s7commHdr->protocol_id = 0x32; + s7commHdr->msg_type = msg_type; + s7commHdr->reserved = 0x0000; + s7commHdr->pdu_ref = htobe16(pdu_ref); + s7commHdr->param_length = htobe16(param_length); + s7commHdr->data_length = htobe16(data_length); + m_Protocol = S7COMM; + } + + std::string S7commLayer::toString() const { + std::ostringstream msgTypeStream; + msgTypeStream << getMsgType(); + std::ostringstream pduRefStream; + pduRefStream << getPduRef(); + std::ostringstream paramLengthStream; + paramLengthStream << getParamLength(); + std::ostringstream dataLengthStream; + dataLengthStream << getDataLength(); + + return "S7comm Layer, msg_type: " + msgTypeStream.str() + + ", pdu_ref: " + pduRefStream.str() + + ", param_length: " + paramLengthStream.str() + + ", data_length: " + dataLengthStream.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()->protocol_id; + } + + uint8_t S7commLayer::getMsgType() const { + return getS7commHeader()->msg_type; + } + + uint16_t S7commLayer::getReserved() const { + return htobe16(getS7commHeader()->reserved); + } + + uint16_t S7commLayer::getParamLength() const { + return htobe16(getS7commHeader()->param_length); + } + + uint16_t S7commLayer::getPduRef() const { + return htobe16(getS7commHeader()->pdu_ref);; + } + + uint16_t S7commLayer::getDataLength() const { + return htobe16(getS7commHeader()->data_length); + } + + void S7commLayer::setMsgType(uint8_t msg_type) const { + getS7commHeader()->msg_type = msg_type; + } + + void S7commLayer::setParamLength(uint16_t param_length) const { + getS7commHeader()->param_length = htobe16(param_length); + } + + void S7commLayer::setPduRef(uint16_t pdu_ref) const { + getS7commHeader()->pdu_ref = htobe16(pdu_ref); + } + + void S7commLayer::setDataLength(uint16_t data_length) const { + getS7commHeader()->data_length = htobe16(data_length); + } +} // namespace pcpp diff --git a/Tests/Packet++Test/CMakeLists.txt b/Tests/Packet++Test/CMakeLists.txt index 3f781b52ac..a77bc4901a 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/TestDefinition.h b/Tests/Packet++Test/TestDefinition.h index d4c1ee8571..ac10603fd7 100644 --- a/Tests/Packet++Test/TestDefinition.h +++ b/Tests/Packet++Test/TestDefinition.h @@ -243,3 +243,6 @@ PTF_TEST_CASE(VrrpCreateAndEditTest); //Implemented in CotpTests.cpp PTF_TEST_CASE(CotpLayerTest); + +//Implemented in S7commTests.cpp +PTF_TEST_CASE(S7commLayerTest); diff --git a/Tests/Packet++Test/Tests/S7commTests.cpp b/Tests/Packet++Test/Tests/S7commTests.cpp new file mode 100644 index 0000000000..6bb41c1de9 --- /dev/null +++ b/Tests/Packet++Test/Tests/S7commTests.cpp @@ -0,0 +1,44 @@ +#include "../TestDefinition.h" +#include "../Utils/TestUtils.h" +#include "EndianPortable.h" +#include "Packet.h" +#include "S7commLayer.h" +#include + +PTF_TEST_CASE(S7commLayerTest) { + timeval time; + gettimeofday(&time, NULL); + + 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(), 0x32); + PTF_ASSERT_EQUAL(s7commLayer->getMsgType(), 0x07); + PTF_ASSERT_EQUAL(s7commLayer->getReserved(), htobe16(0)); + PTF_ASSERT_EQUAL(s7commLayer->getPduRef(), 0xfd0b); + PTF_ASSERT_EQUAL(s7commLayer->getParamLength(), 12); + PTF_ASSERT_EQUAL(s7commLayer->getDataLength(), 212); + + pcpp::S7commLayer newS7commPacket(0x09, 0xfd0c, 13, 213); + + PTF_ASSERT_EQUAL(newS7commPacket.getMsgType(), 0x09); + PTF_ASSERT_EQUAL(newS7commPacket.getPduRef(), 0xfd0c); + PTF_ASSERT_EQUAL(newS7commPacket.getParamLength(), 13); + PTF_ASSERT_EQUAL(newS7commPacket.getDataLength(), 213); + + + newS7commPacket.setMsgType(0x06); + newS7commPacket.setPduRef(0xfd0a); + newS7commPacket.setParamLength(15); + newS7commPacket.setDataLength(215); + + PTF_ASSERT_EQUAL(newS7commPacket.getMsgType(), 0x06); + PTF_ASSERT_EQUAL(newS7commPacket.getPduRef(), 0xfd0a); + PTF_ASSERT_EQUAL(newS7commPacket.getParamLength(), 15); + PTF_ASSERT_EQUAL(newS7commPacket.getDataLength(), 215); + +} // S7commLayerTest diff --git a/Tests/Packet++Test/main.cpp b/Tests/Packet++Test/main.cpp index 731bf31fdd..78e17d1395 100644 --- a/Tests/Packet++Test/main.cpp +++ b/Tests/Packet++Test/main.cpp @@ -316,5 +316,7 @@ int main(int argc, char* argv[]) PTF_RUN_TEST(CotpLayerTest, "cotp"); + PTF_RUN_TEST(S7commLayerTest, "s7comm"); + PTF_END_RUNNING_TESTS; }