modbustcp.go 5.2 KB

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