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 }