blob: 5a481b0db916ba1b2d3040fb5ed1d94dcffe42df [file] [log] [blame]
Naveen Sampath04696f72022-06-13 15:19:14 +05301// Copyright 2018, The GoPacket Authors, All rights reserved.
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the LICENSE file in the root of the source
5// tree.
6//
7//******************************************************************************
8
9package layers
10
11import (
12 "encoding/binary"
13 "errors"
14
15 "github.com/google/gopacket"
16)
17
18//******************************************************************************
19//
20// ModbusTCP Decoding Layer
21// ------------------------------------------
22// This file provides a GoPacket decoding layer for ModbusTCP.
23//
24//******************************************************************************
25
26const mbapRecordSizeInBytes int = 7
27const modbusPDUMinimumRecordSizeInBytes int = 2
28const modbusPDUMaximumRecordSizeInBytes int = 253
29
30// ModbusProtocol type
31type ModbusProtocol uint16
32
33// ModbusProtocol known values.
34const (
35 ModbusProtocolModbus ModbusProtocol = 0
36)
37
38func (mp ModbusProtocol) String() string {
39 switch mp {
40 default:
41 return "Unknown"
42 case ModbusProtocolModbus:
43 return "Modbus"
44 }
45}
46
47//******************************************************************************
48
49// ModbusTCP Type
50// --------
51// Type ModbusTCP implements the DecodingLayer interface. Each ModbusTCP object
52// represents in a structured form the MODBUS Application Protocol header (MBAP) record present as the TCP
53// payload in an ModbusTCP TCP packet.
54//
55type ModbusTCP struct {
56 BaseLayer // Stores the packet bytes and payload (Modbus PDU) bytes .
57
58 TransactionIdentifier uint16 // Identification of a MODBUS Request/Response transaction
59 ProtocolIdentifier ModbusProtocol // It is used for intra-system multiplexing
60 Length uint16 // Number of following bytes (includes 1 byte for UnitIdentifier + Modbus data length
61 UnitIdentifier uint8 // Identification of a remote slave connected on a serial line or on other buses
62}
63
64//******************************************************************************
65
66// LayerType returns the layer type of the ModbusTCP object, which is LayerTypeModbusTCP.
67func (d *ModbusTCP) LayerType() gopacket.LayerType {
68 return LayerTypeModbusTCP
69}
70
71//******************************************************************************
72
73// decodeModbusTCP analyses a byte slice and attempts to decode it as an ModbusTCP
74// record of a TCP packet.
75//
76// If it succeeds, it loads p with information about the packet and returns nil.
77// If it fails, it returns an error (non nil).
78//
79// This function is employed in layertypes.go to register the ModbusTCP layer.
80func decodeModbusTCP(data []byte, p gopacket.PacketBuilder) error {
81
82 // Attempt to decode the byte slice.
83 d := &ModbusTCP{}
84 err := d.DecodeFromBytes(data, p)
85 if err != nil {
86 return err
87 }
88 // If the decoding worked, add the layer to the packet and set it
89 // as the application layer too, if there isn't already one.
90 p.AddLayer(d)
91 p.SetApplicationLayer(d)
92
93 return p.NextDecoder(d.NextLayerType())
94
95}
96
97//******************************************************************************
98
99// DecodeFromBytes analyses a byte slice and attempts to decode it as an ModbusTCP
100// record of a TCP packet.
101//
102// Upon succeeds, it loads the ModbusTCP object with information about the packet
103// and returns nil.
104// Upon failure, it returns an error (non nil).
105func (d *ModbusTCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
106
107 // If the data block is too short to be a MBAP record, then return an error.
108 if len(data) < mbapRecordSizeInBytes+modbusPDUMinimumRecordSizeInBytes {
109 df.SetTruncated()
110 return errors.New("ModbusTCP packet too short")
111 }
112
113 if len(data) > mbapRecordSizeInBytes+modbusPDUMaximumRecordSizeInBytes {
114 df.SetTruncated()
115 return errors.New("ModbusTCP packet too long")
116 }
117
118 // ModbusTCP type embeds type BaseLayer which contains two fields:
119 // Contents is supposed to contain the bytes of the data at this level (MPBA).
120 // Payload is supposed to contain the payload of this level (PDU).
121 d.BaseLayer = BaseLayer{Contents: data[:mbapRecordSizeInBytes], Payload: data[mbapRecordSizeInBytes:len(data)]}
122
123 // Extract the fields from the block of bytes.
124 // The fields can just be copied in big endian order.
125 d.TransactionIdentifier = binary.BigEndian.Uint16(data[:2])
126 d.ProtocolIdentifier = ModbusProtocol(binary.BigEndian.Uint16(data[2:4]))
127 d.Length = binary.BigEndian.Uint16(data[4:6])
128
129 // Length should have the size of the payload plus one byte (size of UnitIdentifier)
130 if d.Length != uint16(len(d.BaseLayer.Payload)+1) {
131 df.SetTruncated()
132 return errors.New("ModbusTCP packet with wrong field value (Length)")
133 }
134 d.UnitIdentifier = uint8(data[6])
135
136 return nil
137}
138
139//******************************************************************************
140
141// NextLayerType returns the layer type of the ModbusTCP payload, which is LayerTypePayload.
142func (d *ModbusTCP) NextLayerType() gopacket.LayerType {
143 return gopacket.LayerTypePayload
144}
145
146//******************************************************************************
147
148// Payload returns Modbus Protocol Data Unit (PDU) composed by Function Code and Data, it is carried within ModbusTCP packets
149func (d *ModbusTCP) Payload() []byte {
150 return d.BaseLayer.Payload
151}
152
153// CanDecode returns the set of layer types that this DecodingLayer can decode
154func (s *ModbusTCP) CanDecode() gopacket.LayerClass {
155 return LayerTypeModbusTCP
156}