github.com/gopacket/gopacket@v1.1.0/layers/modbustcp.go (about)

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