Coverage Report

Created: 2025-09-27 08:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/header/ModbusLayer.h
Line
Count
Source
1
#pragma once
2
3
#include "Layer.h"
4
5
/// @file
6
/// This file contains classes for parsing, creating and editing Modbus packets.
7
8
/// @namespace pcpp
9
/// @brief The main namespace for the PcapPlusPlus lib
10
namespace pcpp
11
{
12
13
#pragma pack(push, 1)
14
  /// @struct modbus_header
15
  /// MODBUS Application Protocol header
16
  struct modbus_header
17
  {
18
    /// For synchronization between messages of server and client
19
    uint16_t transactionId;
20
    /// 0 for Modbus/TCP
21
    uint16_t protocolId;
22
    /// Number of remaining bytes in this frame starting from the unit id
23
    uint16_t length;
24
    /// Unit identifier
25
    uint8_t unitId;
26
    /// Function code
27
    uint8_t functionCode;
28
  };
29
#pragma pack(pop)
30
  static_assert(sizeof(modbus_header) == 8, "modbus_header size is not 8 bytes");
31
32
  /// @class ModbusLayer
33
  /// Represents the MODBUS Application Protocol layer
34
  class ModbusLayer : public Layer
35
  {
36
  public:
37
    /// @brief Enum class representing Modbus function codes.
38
    /// This enumeration defines the standard Modbus function codes used in request and response PDUs.
39
    /// Each value corresponds to a specific operation defined by the Modbus protocol.
40
    enum class ModbusFunctionCode : uint8_t
41
    {
42
      /// Read coil status (0x01)
43
      ReadCoils = 1,
44
45
      /// Read discrete input status (0x02)
46
      ReadDiscreteInputs = 2,
47
48
      /// Read holding registers (0x03)
49
      ReadHoldingRegisters = 3,
50
51
      /// Read input registers (0x04)
52
      ReadInputRegisters = 4,
53
54
      /// Write a single coil (0x05)
55
      WriteSingleCoil = 5,
56
57
      /// Write a single holding register (0x06)
58
      WriteSingleHoldingRegister = 6,
59
60
      /// Write multiple coils (0x0F)
61
      WriteMultipleCoils = 15,
62
63
      /// Write multiple holding registers (0x10)
64
      WriteMultipleHoldingRegisters = 16,
65
66
      /// Report slave ID (0x11)
67
      ReadSlaveId = 17,
68
69
      /// Unknown or unsupported function code (0xFF)
70
      UnknownFunction = 0xFF
71
    };
72
73
    /// @struct ModbusReadInputRegisters
74
    /// Represents a Modbus request to read input registers.
75
    struct ModbusReadInputRegisters
76
    {
77
      uint16_t startingAddress;  ///< Starting address of the input registers to read
78
      uint16_t quantity;         ///< Number of input registers to read
79
    };
80
81
    /// A constructor that creates the layer from an existing packet raw data
82
    /// @param[in] data A pointer to the raw data
83
    /// @param[in] dataLen Size of the data in bytes
84
    /// @param[in] prevLayer A pointer to the previous layer
85
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
86
    ModbusLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
87
0
        : Layer(data, dataLen, prevLayer, packet, Modbus)
88
0
    {}
89
90
    /// A constructor that creates the layer from user inputs
91
    /// @param[in] transactionId Transaction ID
92
    /// @param[in] unitId Unit ID
93
    ModbusLayer(uint16_t transactionId, uint8_t unitId);
94
95
    /// @brief  Check if a port is a valid MODBUS port
96
    /// @param port Port number to check
97
    /// @note MODBUS uses port 502, so this function checks if the port is equal to 502
98
    /// @return true if the port is valid, false otherwise
99
    static bool isModbusPort(uint16_t port)
100
60.0k
    {
101
60.0k
      return port == 502;
102
60.0k
    }
103
104
    /// @return MODBUS message type
105
    uint16_t getTransactionId() const;
106
107
    /// @return MODBUS protocol id
108
    uint16_t getProtocolId() const;
109
110
    /// @return MODBUS remaining bytes in frame starting from the unit id
111
    /// @note This is the length of the MODBUS payload + unit_id, not the entire packet
112
    uint16_t getLength() const;
113
114
    /// @return MODBUS unit id
115
    uint8_t getUnitId() const;
116
117
    /// @return MODBUS function code
118
    ModbusFunctionCode getFunctionCode() const;
119
120
    /// @brief set the MODBUS transaction id
121
    /// @param transactionId transaction id
122
    void setTransactionId(uint16_t transactionId);
123
124
    /// @brief set the MODBUS header unit id
125
    /// @param unitId unit id
126
    void setUnitId(uint8_t unitId);
127
128
    /// @brief set the MODBUS header function code
129
    /// @param functionCode function code
130
    void setFunctionCode(ModbusFunctionCode functionCode);
131
132
    // Overridden methods
133
134
    /// Does nothing for this layer (ModbusLayer is always last)
135
    void parseNextLayer() override
136
0
    {}
137
138
    /// @brief Get the length of the MODBUS header
139
    /// @return Length of the MODBUS header in bytes
140
    size_t getHeaderLen() const override
141
0
    {
142
0
      return sizeof(modbus_header);
143
0
    }
144
145
    /// Does nothing for this layer
146
    void computeCalculateFields() override
147
0
    {}
148
149
    /// @return A string representation of the layer most important data (should look like the layer description in
150
    /// Wireshark)
151
    std::string toString() const override;
152
153
    /// @return The OSI Model layer this protocol belongs to
154
    OsiModelLayer getOsiModelLayer() const override
155
0
    {
156
0
      return OsiModelApplicationLayer;
157
0
    }
158
159
  private:
160
    /// @return A pointer to the MODBUS header
161
    modbus_header* getModbusHeader() const;
162
  };
163
164
}  // namespace pcpp