123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- // 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
- }
|