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 151 152 153 154 155
|
// 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/gopacket/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
}
// CanDecode returns the set of layer types that this DecodingLayer can decode
func (s *ModbusTCP) CanDecode() gopacket.LayerClass {
return LayerTypeModbusTCP
}
|