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