-
Notifications
You must be signed in to change notification settings - Fork 722
#1754 Add Modbus Support #1823
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
#1754 Add Modbus Support #1823
Changes from all commits
Commits
Show all changes
54 commits
Select commit
Hold shift + click to select a range
a36e594
Add ModbusLayer first implementation with header parsing only
yahyayozo 88f69d4
fix static_assert
yahyayozo c257494
Merge branch 'dev' into feature/modbus
yahyayozo 348afc6
Fix formatting in ModbusLayer header file
yahyayozo 1f65c24
Add setter methods for MODBUS header fields in ModbusLayer
yahyayozo 4159c6c
Merge branch 'dev' into feature/modbus
yahyayozo 58c18ec
Refactor ModbusLayer class documentation and fix syntax error
yahyayozo d50db3b
Merge branch 'feature/modbus' of https://github.com/yahyayozo/PcapPlu…
yahyayozo cfcbc69
Fix endianess handling in ModbusLayer getters and setters
yahyayozo 28dd176
Add const qualifier to ModbusLayer getter methods
yahyayozo 34d65a2
Add constructors to ModbusLayer for initializing from user inputs
yahyayozo 74ab79d
Add Modbus protocol constant to ProtocolType
yahyayozo 166c15c
Add const qualifier to ModbusLayer getter methods in .h file
yahyayozo 3624286
Add ModbusLayer and corresponding tests to Packet++Test
yahyayozo bcd14b5
Fix length assignment in ModbusLayer constructor to use htobe16
yahyayozo 9f4ebbd
Refactor ModbusLayerParsingTest to create ModbusLayer from a dat file…
yahyayozo 4ceb41d
Fix formatting in ModbusLayer constructor for improved readability
yahyayozo adb3f51
Remove redundant ModbusLayerParsingTest case from ModbusTests.cpp
yahyayozo 839aa49
Remove ModbusLayerParsingTest from test definitions and main execution
yahyayozo dc04c0c
Fix newline at end of file in ModbusLayerCreationTest and TestDefinit…
yahyayozo f4f5291
Add ModbusLayer parsing test and utility function for port validation
yahyayozo 24c0924
Merge branch 'dev' into feature/modbus
yahyayozo 32da5e5
Add missing includes for iostream and iomanip in ModbusLayer.cpp
yahyayozo da52bf7
Refactor ModbusLayerCreationTest to include additional assertions for…
yahyayozo c8bd099
Merge branch 'feature/modbus' of https://github.com/yahyayozo/PcapPlu…
yahyayozo b96671c
Merge branch 'dev' into feature/modbus
yahyayozo 5dd18fb
Merge branch 'dev' into feature/modbus
yahyayozo 4b38a65
Refactor Modbus header structure and update related methods for consi…
yahyayozo 65b37f6
Merge branch 'feature/modbus' of https://github.com/yahyayozo/PcapPlu…
yahyayozo 32211b8
Fix ModbusLayer::toString method declaration for consistency
yahyayozo 0f001b6
Update ModbusRequest.dat and ModbusTests.cpp for improved test accura…
yahyayozo adea8b5
Add modbus.pcap example file for packet testing
yahyayozo 530c8cc
Refactor Modbus function code handling for type consistency
yahyayozo 73228fb
Merge branch 'dev' into feature/modbus
yahyayozo 12b1650
adjust modbus creation test
yahyayozo 494d84a
formatting
yahyayozo 737e6de
Merge branch 'dev' into feature/modbus
yahyayozo 9b50332
chore: add Modbus to readme
yahyayozo 75dff34
add the modbus function code as an enum clas
yahyayozo a5b13c7
added struct for read coil function
yahyayozo 3e56991
Merge branch 'dev' into feature/modbus
yahyayozo 18a80df
refactor
yahyayozo 3c746e9
Merge branch 'dev' into feature/modbus
yahyayozo ec3e582
fix: wrong header length in modbus
yahyayozo 4a8f115
Merge branch 'feature/modbus' of https://github.com/yahyayozo/PcapPlu…
yahyayozo c50f58b
some refactoring
yahyayozo ea86788
Merge branch 'dev' into feature/modbus
yahyayozo 0cd7502
formatting
yahyayozo fc595a7
formatting
yahyayozo 951fe03
style
yahyayozo 93fb5e7
refactor: adjust comment style and enum case
yahyayozo a7b926d
refactor
yahyayozo 8bc7c47
refactor: fixing comments
yahyayozo e3461d0
fix: remove _deps
yahyayozo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| #pragma once | ||
|
|
||
| #include "Layer.h" | ||
|
|
||
| /// @file | ||
| /// This file contains classes for parsing, creating and editing Modbus packets. | ||
|
|
||
| /// @namespace pcpp | ||
| /// @brief The main namespace for the PcapPlusPlus lib | ||
| namespace pcpp | ||
| { | ||
|
|
||
| #pragma pack(push, 1) | ||
| /// @struct modbus_header | ||
| /// MODBUS Application Protocol header | ||
| struct modbus_header | ||
| { | ||
| /// For synchronization between messages of server and client | ||
| uint16_t transactionId; | ||
| /// 0 for Modbus/TCP | ||
| uint16_t protocolId; | ||
| /// Number of remaining bytes in this frame starting from the unit id | ||
| uint16_t length; | ||
| /// Unit identifier | ||
| uint8_t unitId; | ||
| /// Function code | ||
| uint8_t functionCode; | ||
| }; | ||
| #pragma pack(pop) | ||
| static_assert(sizeof(modbus_header) == 8, "modbus_header size is not 8 bytes"); | ||
|
|
||
| /// @class ModbusLayer | ||
| /// Represents the MODBUS Application Protocol layer | ||
| class ModbusLayer : public Layer | ||
| { | ||
| public: | ||
| /// @brief Enum class representing Modbus function codes. | ||
| /// This enumeration defines the standard Modbus function codes used in request and response PDUs. | ||
| /// Each value corresponds to a specific operation defined by the Modbus protocol. | ||
| enum class ModbusFunctionCode : uint8_t | ||
| { | ||
| /// Read coil status (0x01) | ||
| ReadCoils = 1, | ||
|
|
||
| /// Read discrete input status (0x02) | ||
| ReadDiscreteInputs = 2, | ||
|
|
||
| /// Read holding registers (0x03) | ||
| ReadHoldingRegisters = 3, | ||
|
|
||
| /// Read input registers (0x04) | ||
| ReadInputRegisters = 4, | ||
|
|
||
| /// Write a single coil (0x05) | ||
| WriteSingleCoil = 5, | ||
|
|
||
| /// Write a single holding register (0x06) | ||
| WriteSingleHoldingRegister = 6, | ||
|
|
||
| /// Write multiple coils (0x0F) | ||
| WriteMultipleCoils = 15, | ||
|
|
||
| /// Write multiple holding registers (0x10) | ||
| WriteMultipleHoldingRegisters = 16, | ||
|
|
||
| /// Report slave ID (0x11) | ||
| ReadSlaveId = 17, | ||
|
|
||
| /// Unknown or unsupported function code (0xFF) | ||
| UnknownFunction = 0xFF | ||
| }; | ||
|
|
||
| /// @struct ModbusReadInputRegisters | ||
| /// Represents a Modbus request to read input registers. | ||
| struct ModbusReadInputRegisters | ||
| { | ||
| uint16_t startingAddress; ///< Starting address of the input registers to read | ||
| uint16_t quantity; ///< Number of input registers to read | ||
| }; | ||
|
|
||
| /// A constructor that creates the layer from an existing packet raw data | ||
| /// @param[in] data A pointer to the raw data | ||
| /// @param[in] dataLen Size of the data in bytes | ||
| /// @param[in] prevLayer A pointer to the previous layer | ||
| /// @param[in] packet A pointer to the Packet instance where layer will be stored in | ||
| ModbusLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) | ||
| : Layer(data, dataLen, prevLayer, packet, Modbus) | ||
| {} | ||
|
|
||
| /// A constructor that creates the layer from user inputs | ||
| /// @param[in] transactionId Transaction ID | ||
| /// @param[in] unitId Unit ID | ||
| ModbusLayer(uint16_t transactionId, uint8_t unitId); | ||
|
|
||
| /// @brief Check if a port is a valid MODBUS port | ||
| /// @param port Port number to check | ||
| /// @note MODBUS uses port 502, so this function checks if the port is equal to 502 | ||
| /// @return true if the port is valid, false otherwise | ||
| static bool isModbusPort(uint16_t port) | ||
| { | ||
| return port == 502; | ||
| } | ||
|
|
||
| /// @return MODBUS message type | ||
| uint16_t getTransactionId() const; | ||
|
|
||
| /// @return MODBUS protocol id | ||
| uint16_t getProtocolId() const; | ||
|
|
||
| /// @return MODBUS remaining bytes in frame starting from the unit id | ||
| /// @note This is the length of the MODBUS payload + unit_id, not the entire packet | ||
| uint16_t getLength() const; | ||
|
|
||
| /// @return MODBUS unit id | ||
| uint8_t getUnitId() const; | ||
|
|
||
| /// @return MODBUS function code | ||
| ModbusFunctionCode getFunctionCode() const; | ||
|
|
||
| /// @brief set the MODBUS transaction id | ||
| /// @param transactionId transaction id | ||
| void setTransactionId(uint16_t transactionId); | ||
|
|
||
| /// @brief set the MODBUS header unit id | ||
| /// @param unitId unit id | ||
| void setUnitId(uint8_t unitId); | ||
|
|
||
| /// @brief set the MODBUS header function code | ||
| /// @param functionCode function code | ||
| void setFunctionCode(ModbusFunctionCode functionCode); | ||
|
|
||
| // Overridden methods | ||
|
|
||
| /// Does nothing for this layer (ModbusLayer is always last) | ||
| void parseNextLayer() override | ||
| {} | ||
|
|
||
| /// @brief Get the length of the MODBUS header | ||
| /// @return Length of the MODBUS header in bytes | ||
| size_t getHeaderLen() const override | ||
| { | ||
| return sizeof(modbus_header); | ||
| } | ||
|
|
||
| /// Does nothing for this layer | ||
| void computeCalculateFields() override | ||
| {} | ||
|
|
||
| /// @return A string representation of the layer most important data (should look like the layer description in | ||
| /// Wireshark) | ||
| std::string toString() const override; | ||
|
|
||
| /// @return The OSI Model layer this protocol belongs to | ||
| OsiModelLayer getOsiModelLayer() const override | ||
| { | ||
| return OsiModelApplicationLayer; | ||
| } | ||
|
|
||
| private: | ||
| /// @return A pointer to the MODBUS header | ||
| modbus_header* getModbusHeader() const; | ||
| }; | ||
|
|
||
| } // namespace pcpp | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| #include "ModbusLayer.h" | ||
| #include "EndianPortable.h" | ||
| #include <iostream> | ||
| #include <iomanip> | ||
| #include <cstring> | ||
| #include "Logger.h" | ||
|
|
||
| namespace pcpp | ||
| { | ||
| ModbusLayer::ModbusLayer(uint16_t transactionId, uint8_t unitId) | ||
| { | ||
| const int16_t pduSize = sizeof(ModbusReadInputRegisters); // Currently only supporting Read Input Registers | ||
| const size_t headerLen = sizeof(modbus_header); | ||
|
|
||
| m_DataLen = headerLen + pduSize; | ||
| m_Data = new uint8_t[m_DataLen]{}; | ||
| memset(m_Data, 0, m_DataLen); | ||
|
|
||
| // Initialize the header fields to default values | ||
| modbus_header* header = getModbusHeader(); | ||
| header->transactionId = htobe16(transactionId); | ||
| header->protocolId = 0; // 0 for Modbus/TCP | ||
| header->length = htobe16(pduSize + 2); // Length includes unitId and functionCode | ||
| header->unitId = unitId; | ||
| header->functionCode = static_cast<uint8_t>(ModbusFunctionCode::ReadInputRegisters); | ||
| } | ||
|
|
||
| modbus_header* ModbusLayer::getModbusHeader() const | ||
| { | ||
| return (modbus_header*)m_Data; | ||
| } | ||
|
|
||
| uint16_t ModbusLayer::getTransactionId() const | ||
| { | ||
| return be16toh(getModbusHeader()->transactionId); | ||
| } | ||
|
|
||
| uint16_t ModbusLayer::getProtocolId() const | ||
| { | ||
| return be16toh(getModbusHeader()->protocolId); | ||
| } | ||
|
|
||
| uint16_t ModbusLayer::getLength() const | ||
| { | ||
| return be16toh(getModbusHeader()->length); | ||
| } | ||
|
|
||
| uint8_t ModbusLayer::getUnitId() const | ||
| { | ||
| return getModbusHeader()->unitId; | ||
| } | ||
|
|
||
| ModbusLayer::ModbusFunctionCode ModbusLayer::getFunctionCode() const | ||
| { | ||
| switch (getModbusHeader()->functionCode) | ||
| { | ||
| case 1: | ||
| return ModbusFunctionCode::ReadCoils; | ||
| case 2: | ||
| return ModbusFunctionCode::ReadDiscreteInputs; | ||
| case 3: | ||
| return ModbusFunctionCode::ReadHoldingRegisters; | ||
| case 4: | ||
| return ModbusFunctionCode::ReadInputRegisters; | ||
| case 5: | ||
| return ModbusFunctionCode::WriteSingleCoil; | ||
| case 6: | ||
| return ModbusFunctionCode::WriteSingleHoldingRegister; | ||
| case 15: | ||
| return ModbusFunctionCode::WriteMultipleCoils; | ||
| case 16: | ||
| return ModbusFunctionCode::WriteMultipleHoldingRegisters; | ||
| case 17: | ||
| return ModbusFunctionCode::ReadSlaveId; | ||
| default: | ||
| return ModbusFunctionCode::UnknownFunction; | ||
| } | ||
| } | ||
|
|
||
| void ModbusLayer::setTransactionId(uint16_t transactionId) | ||
| { | ||
| getModbusHeader()->transactionId = htobe16(transactionId); | ||
| } | ||
|
|
||
| void ModbusLayer::setUnitId(uint8_t unitId) | ||
| { | ||
| getModbusHeader()->unitId = unitId; | ||
| } | ||
|
|
||
| void ModbusLayer::setFunctionCode(ModbusLayer::ModbusFunctionCode functionCode) | ||
| { | ||
| getModbusHeader()->functionCode = static_cast<uint8_t>(functionCode); | ||
| } | ||
|
|
||
| std::string ModbusLayer::toString() const | ||
| { | ||
| return "Modbus Layer, Transaction ID: " + std::to_string(getTransactionId()) + | ||
| ", Protocol ID: " + std::to_string(getProtocolId()) + ", Length: " + std::to_string(getLength()) + | ||
| ", Unit ID: " + std::to_string(getUnitId()) + | ||
| ", Function Code: " + std::to_string(static_cast<uint8_t>(getFunctionCode())); | ||
| } | ||
| } // namespace pcpp |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 000c29af7ffe109add4e060d080045000040c6e64000400658880a0101ea0a0a0555c8d301f6e072efbac405c33a8018ffffd5d900000101080a37c3fee900ba2a0f001100000006ff0402580064 |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This struct is only used in
getFunctionDataSize(). If we remove it we can remove this struct as well