Home | History | Annotate | Download | only in contrib
      1 % Modbus layer test campaign
      2 
      3 + Syntax check
      4 = Import the modbus layer
      5 from scapy.contrib.modbus import *
      6 
      7 + Test MBAP
      8 = MBAP default values
      9 raw(ModbusADURequest()) == b'\x00\x00\x00\x00\x00\x01\xff'
     10 
     11 = MBAP payload length calculation
     12 raw(ModbusADURequest() / b'\x00\x01\x02') == b'\x00\x00\x00\x00\x00\x04\xff\x00\x01\x02'
     13 
     14 = MBAP Guess Payload ModbusPDU01ReadCoilsRequest (simple case)
     15 p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x06\xff\x01\x00\x00\x00\x01')
     16 assert(isinstance(p.payload, ModbusPDU01ReadCoilsRequest))
     17 = MBAP Guess Payload ModbusPDU01ReadCoilsResponse
     18 p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x04\xff\x01\x01\x01')
     19 assert(isinstance(p.payload, ModbusPDU01ReadCoilsResponse))
     20 = MBAP Guess Payload ModbusPDU01ReadCoilsError
     21 p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x03\xff\x81\x02')
     22 assert(isinstance(p.payload, ModbusPDU01ReadCoilsError))
     23 
     24 = MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationRequest (2 level test)
     25 p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x04\xff+\x0e\x01\x00')
     26 assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationRequest))
     27 = MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationResponse
     28 p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x1b\xff+\x0e\x01\x83\x00\x00\x03\x00\x08Pymodbus\x01\x02PM\x02\x031.0')
     29 assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationResponse))
     30 = MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationError
     31 p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x03\xff\xab\x01')
     32 assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationError))
     33 
     34 = MBAP Guess Payload Reserved Function Request (Invalid payload)
     35 p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x02\xff\x5b')
     36 assert(isinstance(p.payload,ModbusPDUReservedFunctionCodeRequest))
     37 = MBAP Guess Payload Reserved Function Response (Invalid payload)
     38 p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x7e')
     39 assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeResponse))
     40 = MBAP Guess Payload Reserved Function Error (Invalid payload)
     41 p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x8a')
     42 assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeError))
     43 
     44 = MBAP Guess Payload ModbusPDU02ReadDiscreteInputsResponse
     45 assert(raw(ModbusPDU02ReadDiscreteInputsResponse()), b'\x02\x01\x00')
     46 = MBAP Guess Payload ModbusPDU02ReadDiscreteInputsResponse minimal parameters
     47 assert(raw(ModbusPDU02ReadDiscreteInputsResponse(inputStatus=[0x02, 0x01])), b'\x02\x02\x02\x01')
     48 = MBAP Guess Payload ModbusPDU02ReadDiscreteInputsRequest dissection
     49 p = ModbusPDU02ReadDiscreteInputsResponse(b'\x02\x02\x02\x01')
     50 p.byteCount == 2 and p.inputStatus == [0x02, 0x01]
     51 
     52 = ModbusPDU02ReadDiscreteInputsError
     53 raw(ModbusPDU02ReadDiscreteInputsError()) == b'\x82\x01'
     54 
     55 = MBAP Guess Payload User-Defined Function Request (Invalid payload)
     56 p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x02\xff\x5b')
     57 assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeRequest))
     58 = MBAP Guess Payload User-Defined Function Response (Invalid payload)
     59 p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x7e')
     60 assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeResponse))
     61 = MBAP Guess Payload User-Defined Function Error (Invalid payload)
     62 p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x8a')
     63 assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeError))
     64 
     65 + Test layer binding
     66 = Destination port
     67 p = TCP()/ModbusADURequest()
     68 p[TCP].dport == 502
     69 
     70 = Source port
     71 p = TCP()/ModbusADUResponse()
     72 p[TCP].sport == 502
     73 
     74 + Test PDU
     75 * Note on tests cases: dissection/minimal parameters will not be done for packets that does not perform calculation
     76 # 0x01/0x81 Read Coils --------------------------------------------------------------
     77 = ModbusPDU01ReadCoilsRequest
     78 raw(ModbusPDU01ReadCoilsRequest()) == b'\x01\x00\x00\x00\x01'
     79 = ModbusPDU01ReadCoilsRequest minimal parameters
     80 raw(ModbusPDU01ReadCoilsRequest(startAddr=16, quantity=2)) == b'\x01\x00\x10\x00\x02'
     81 = ModbusPDU01ReadCoilsRequest dissection
     82 p = ModbusPDU01ReadCoilsRequest(b'\x01\x00\x10\x00\x02')
     83 assert(p.startAddr == 16)
     84 assert(p.quantity == 2)
     85 
     86 = ModbusPDU01ReadCoilsResponse
     87 raw(ModbusPDU01ReadCoilsResponse()) == b'\x01\x01\x00'
     88 = ModbusPDU01ReadCoilsResponse minimal parameters
     89 raw(ModbusPDU01ReadCoilsResponse(coilStatus=[0x10]*3)) == b'\x01\x03\x10\x10\x10'
     90 = ModbusPDU01ReadCoilsResponse dissection
     91 p = ModbusPDU01ReadCoilsResponse(b'\x01\x03\x10\x10\x10')
     92 assert(p.coilStatus == [16, 16, 16])
     93 assert(p.byteCount == 3)
     94 
     95 = ModbusPDU01ReadCoilsError
     96 raw(ModbusPDU01ReadCoilsError()) == b'\x81\x01'
     97 = ModbusPDU81ReadCoilsError minimal parameters
     98 raw(ModbusPDU01ReadCoilsError(exceptCode=2)) == b'\x81\x02'
     99 = ModbusPDU81ReadCoilsError dissection
    100 p = ModbusPDU01ReadCoilsError(b'\x81\x02')
    101 assert(p.funcCode == 0x81)
    102 assert(p.exceptCode == 2)
    103 
    104 # 0x02/0x82 Read Discrete Inputs Registers ------------------------------------------
    105 = ModbusPDU02ReadDiscreteInputsRequest
    106 raw(ModbusPDU02ReadDiscreteInputsRequest()) == b'\x02\x00\x00\x00\x01'
    107 = ModbusPDU02ReadDiscreteInputsRequest minimal parameters
    108 raw(ModbusPDU02ReadDiscreteInputsRequest(startAddr=8, quantity=128)) == b'\x02\x00\x08\x00\x80'
    109 
    110 = ModbusPDU02ReadDiscreteInputsResponse
    111 raw(ModbusPDU02ReadDiscreteInputsResponse()) == b'\x02\x01\x00'
    112 = ModbusPDU02ReadDiscreteInputsResponse minimal parameters
    113 raw(ModbusPDU02ReadDiscreteInputsResponse(inputStatus=[0x02, 0x01])) == b'\x02\x02\x02\x01'
    114 = ModbusPDU02ReadDiscreteInputsRequest dissection
    115 p = ModbusPDU02ReadDiscreteInputsResponse(b'\x02\x02\x02\x01')
    116 assert(p.byteCount == 2)
    117 assert(p.inputStatus == [0x02, 0x01])
    118 
    119 = ModbusPDU02ReadDiscreteInputsError
    120 raw(ModbusPDU02ReadDiscreteInputsError()) == b'\x82\x01'
    121 
    122 # 0x03/0x83 Read Holding Registers --------------------------------------------------
    123 = ModbusPDU03ReadHoldingRegistersRequest
    124 raw(ModbusPDU03ReadHoldingRegistersRequest()) == b'\x03\x00\x00\x00\x01'
    125 = ModbusPDU03ReadHoldingRegistersRequest minimal parameters
    126 raw(ModbusPDU03ReadHoldingRegistersRequest(startAddr=2048, quantity=16)) == b'\x03\x08\x00\x00\x10'
    127 
    128 = ModbusPDU03ReadHoldingRegistersResponse
    129 raw(ModbusPDU03ReadHoldingRegistersResponse()) == b'\x03\x02\x00\x00'
    130 = ModbusPDU03ReadHoldingRegistersResponse minimal parameters
    131 1==1
    132 = ModbusPDU03ReadHoldingRegistersResponse dissection
    133 p = ModbusPDU03ReadHoldingRegistersResponse(b'\x03\x06\x02+\x00\x00\x00d')
    134 assert(p.byteCount == 6)
    135 assert(p.registerVal == [555, 0, 100])
    136 
    137 = ModbusPDU03ReadHoldingRegistersError
    138 raw(ModbusPDU03ReadHoldingRegistersError()) == b'\x83\x01'
    139 
    140 # 0x04/0x84 Read Input Register -----------------------------------------------------
    141 = ModbusPDU04ReadInputRegistersRequest
    142 raw(ModbusPDU04ReadInputRegistersRequest()) == b'\x04\x00\x00\x00\x01'
    143 
    144 = ModbusPDU04ReadInputRegistersResponse
    145 raw(ModbusPDU04ReadInputRegistersResponse()) == b'\x04\x02\x00\x00'
    146 = ModbusPDU04ReadInputRegistersResponse minimal parameters
    147 raw(ModbusPDU04ReadInputRegistersResponse(registerVal=[0x01, 0x02])) == b'\x04\x04\x00\x01\x00\x02'
    148 
    149 = ModbusPDU04ReadInputRegistersError
    150 raw(ModbusPDU04ReadInputRegistersError()) == b'\x84\x01'
    151 
    152 # 0x05/0x85 Write Single Coil -------------------------------------------------------
    153 = ModbusPDU05WriteSingleCoilRequest
    154 raw(ModbusPDU05WriteSingleCoilRequest()) == b'\x05\x00\x00\x00\x00'
    155 
    156 = ModbusPDU05WriteSingleCoilResponse
    157 raw(ModbusPDU05WriteSingleCoilResponse()) == b'\x05\x00\x00\x00\x00'
    158 
    159 = ModbusPDU05WriteSingleCoilError
    160 raw(ModbusPDU05WriteSingleCoilError()) == b'\x85\x01'
    161 
    162 # 0x06/0x86 Write Single Register ---------------------------------------------------
    163 = ModbusPDU06WriteSingleRegisterError
    164 raw(ModbusPDU06WriteSingleRegisterRequest()) == b'\x06\x00\x00\x00\x00'
    165 
    166 = ModbusPDU06WriteSingleRegisterResponse
    167 raw(ModbusPDU06WriteSingleRegisterResponse()) == b'\x06\x00\x00\x00\x00'
    168 
    169 = ModbusPDU06WriteSingleRegisterError
    170 raw(ModbusPDU06WriteSingleRegisterError()) == b'\x86\x01'
    171 
    172 # 0x07/0x87 Read Exception Status (serial line only) --------------------------------
    173 # 0x08/0x88 Diagnostics (serial line only) ------------------------------------------
    174 # 0x0b Get Comm Event Counter: serial line only -------------------------------------
    175 # 0x0c Get Comm Event Log: serial line only -----------------------------------------
    176 
    177 # 0x0f/0x8f Write Multiple Coils ----------------------------------------------------
    178 = ModbusPDU0FWriteMultipleCoilsRequest
    179 raw(ModbusPDU0FWriteMultipleCoilsRequest())
    180 = ModbusPDU0FWriteMultipleCoilsRequest minimal parameters
    181 raw(ModbusPDU0FWriteMultipleCoilsRequest(outputsValue=[0x01, 0x01])) == b'\x0f\x00\x00\x00\x01\x02\x01\x01'
    182 
    183 = ModbusPDU0FWriteMultipleCoilsResponse
    184 raw(ModbusPDU0FWriteMultipleCoilsResponse()) == b'\x0f\x00\x00\x00\x01'
    185 
    186 = ModbusPDU0FWriteMultipleCoilsError
    187 raw(ModbusPDU0FWriteMultipleCoilsError()) == b'\x8f\x01'
    188 
    189 # 0x10/0x90 Write Multiple Registers ----------------------------------------------------
    190 = ModbusPDU10WriteMultipleRegistersRequest
    191 raw(ModbusPDU10WriteMultipleRegistersRequest()) == b'\x10\x00\x00\x00\x01\x02\x00\x00'
    192 = ModbusPDU10WriteMultipleRegistersRequest minimal parameters
    193 raw(ModbusPDU10WriteMultipleRegistersRequest(outputsValue=[0x0001, 0x0002])) == b'\x10\x00\x00\x00\x02\x04\x00\x01\x00\x02'
    194 
    195 = ModbusPDU10WriteMultipleRegistersResponse
    196 raw(ModbusPDU10WriteMultipleRegistersResponse()) == b'\x10\x00\x00\x00\x01'
    197 
    198 = ModbusPDU10WriteMultipleRegistersError
    199 raw(ModbusPDU10WriteMultipleRegistersError()) == b'\x90\x01'
    200 
    201 # 0x11/91 Report Server ID: serial line only ----------------------------------------
    202 
    203 # 0x14/944 Read File Record ---------------------------------------------------------
    204 = ModbusPDU14ReadFileRecordRequest len parameters
    205 p = raw(ModbusPDU14ReadFileRecordRequest()/ModbusReadFileSubRequest()/ModbusReadFileSubRequest())
    206 assert(p == b'\x14\x0e\x06\x00\x01\x00\x00\x00\x01\x06\x00\x01\x00\x00\x00\x01')
    207 = ModbusPDU14ReadFileRecordRequest minimal parameters
    208 p = raw(ModbusPDU14ReadFileRecordRequest()/ModbusReadFileSubRequest(fileNumber=4, recordNumber=1, recordLength=2)/ModbusReadFileSubRequest(fileNumber=3, recordNumber=9, recordLength=2))
    209 assert(p == b'\x14\x0e\x06\x00\x04\x00\x01\x00\x02\x06\x00\x03\x00\t\x00\x02')
    210 = ModbusPDU14ReadFileRecordRequest dissection
    211 p = ModbusPDU14ReadFileRecordRequest(b'\x14\x0e\x06\x00\x04\x00\x01\x00\x02\x06\x00\x03\x00\t\x00\x02')
    212 assert(isinstance(p.payload, ModbusReadFileSubRequest))
    213 assert(isinstance(p.payload.payload, ModbusReadFileSubRequest))
    214 
    215 = ModbusPDU14ReadFileRecordResponse minimal parameters
    216 raw(ModbusPDU14ReadFileRecordResponse()/ModbusReadFileSubResponse(recData=[0x0dfe, 0x0020])/ModbusReadFileSubResponse(recData=[0x33cd, 0x0040])) == b'\x14\x0c\x05\x06\r\xfe\x00 \x05\x063\xcd\x00@'
    217 = ModbusPDU14ReadFileRecordResponse dissection
    218 p = ModbusPDU14ReadFileRecordResponse(b'\x14\x0c\x05\x06\r\xfe\x00 \x05\x063\xcd\x00@')
    219 assert(isinstance(p.payload, ModbusReadFileSubResponse))
    220 assert(isinstance(p.payload.payload, ModbusReadFileSubResponse))
    221 
    222 = ModbusPDU14ReadFileRecordError
    223 raw(ModbusPDU14ReadFileRecordError()) == b'\x94\x01'
    224 
    225 # 0x15/0x95 Write File Record -------------------------------------------------------
    226 = ModbusPDU15WriteFileRecordRequest minimal parameters
    227 raw(ModbusPDU15WriteFileRecordRequest()/ModbusWriteFileSubRequest(fileNumber=4, recordNumber=7, recordData=[0x06af, 0x04be, 0x100d])) == b'\x15\r\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r'
    228 = ModbusPDU15WriteFileRecordRequest dissection
    229 p = ModbusPDU15WriteFileRecordRequest(b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r')
    230 assert(isinstance(p.payload, ModbusWriteFileSubRequest))
    231 assert(p.payload.recordLength == 3)
    232 
    233 = ModbusPDU15WriteFileRecordResponse minimal parameters
    234 raw(ModbusPDU15WriteFileRecordResponse()/ModbusWriteFileSubResponse(fileNumber=4, recordNumber=7, recordData=[0x06af, 0x04be, 0x100d])) == b'\x15\r\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r'
    235 = ModbusPDU15WriteFileRecordResponse dissection
    236 p = ModbusPDU15WriteFileRecordResponse(b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r')
    237 assert(isinstance(p.payload, ModbusWriteFileSubResponse))
    238 assert(p.payload.recordLength == 3)
    239 
    240 = ModbusPDU15WriteFileRecordError
    241 raw(ModbusPDU15WriteFileRecordError()) == b'\x95\x01'
    242 
    243 # 0x16/0x96 Mask Write Register -----------------------------------------------------
    244 = ModbusPDU16MaskWriteRegisterRequest
    245 raw(ModbusPDU16MaskWriteRegisterRequest()) == b'\x16\x00\x00\xff\xff\x00\x00'
    246 
    247 = ModbusPDU16MaskWriteRegisterResponse
    248 raw(ModbusPDU16MaskWriteRegisterResponse()) == b'\x16\x00\x00\xff\xff\x00\x00'
    249 
    250 = ModbusPDU16MaskWriteRegisterError
    251 raw(ModbusPDU16MaskWriteRegisterError()) == b'\x96\x01'
    252 
    253 # 0x17/0x97 Read/Write Multiple Registers -------------------------------------------
    254 = ModbusPDU17ReadWriteMultipleRegistersRequest
    255 raw(ModbusPDU17ReadWriteMultipleRegistersRequest()) == b'\x17\x00\x00\x00\x01\x00\x00\x00\x01\x02\x00\x00'
    256 = ModbusPDU17ReadWriteMultipleRegistersRequest minimal parameters
    257 raw(ModbusPDU17ReadWriteMultipleRegistersRequest(writeRegistersValue=[0x0001, 0x0002])) == b'\x17\x00\x00\x00\x01\x00\x00\x00\x02\x04\x00\x01\x00\x02'
    258 = ModbusPDU17ReadWriteMultipleRegistersRequest dissection
    259 p = ModbusPDU17ReadWriteMultipleRegistersRequest(b'\x17\x00\x00\x00\x01\x00\x00\x00\x02\x04\x00\x01\x00\x02')
    260 assert(p.byteCount == 4)
    261 assert(p.writeQuantityRegisters == 2)
    262 
    263 = ModbusPDU17ReadWriteMultipleRegistersResponse
    264 raw(ModbusPDU17ReadWriteMultipleRegistersResponse()) == b'\x17\x02\x00\x00'
    265 = ModbusPDU17ReadWriteMultipleRegistersResponse minimal parameters
    266 raw(ModbusPDU17ReadWriteMultipleRegistersResponse(registerVal=[1,2,3])) == b'\x17\x06\x00\x01\x00\x02\x00\x03'
    267 = ModbusPDU17ReadWriteMultipleRegistersResponse dissection
    268 raw(ModbusPDU17ReadWriteMultipleRegistersResponse(b'\x17\x02\x00\x01')) == b'\x17\x02\x00\x01'
    269 
    270 = ModbusPDU17ReadWriteMultipleRegistersError
    271 raw(ModbusPDU17ReadWriteMultipleRegistersError()) == b'\x97\x01'
    272 
    273 # 0x18/0x88 Read FIFO Queue ---------------------------------------------------------
    274 = ModbusPDU18ReadFIFOQueueRequest
    275 raw(ModbusPDU18ReadFIFOQueueRequest()) == b'\x18\x00\x00'
    276 
    277 = ModbusPDU18ReadFIFOQueueResponse
    278 = ModbusPDU18ReadFIFOQueueResponse
    279 raw(ModbusPDU18ReadFIFOQueueResponse()) == b'\x18\x00\x02\x00\x00'
    280 = ModbusPDU18ReadFIFOQueueResponse minimal parameters
    281 raw(ModbusPDU18ReadFIFOQueueResponse(FIFOVal=[0x0001, 0x0002, 0x0003])) == b'\x18\x00\x08\x00\x03\x00\x01\x00\x02\x00\x03'
    282 = ModbusPDU18ReadFIFOQueueResponse dissection
    283 p = ModbusPDU18ReadFIFOQueueResponse(b'\x18\x00\x08\x00\x03\x00\x01\x00\x02\x00\x03')
    284 assert(p.byteCount == 8)
    285 assert(p.FIFOCount == 3)
    286 
    287 = ModbusPDU18ReadFIFOQueueError
    288 raw(ModbusPDU18ReadFIFOQueueError()) == b'\x98\x01'
    289 
    290 # 0x2b encapsulated Interface Transport ---------------------------------------------
    291 # 0x2b 0xOD CANopen General Reference (out of the main specification) ---------------
    292 
    293 # 0x2b 0xOE Read Device Information -------------------------------------------------
    294 = ModbusPDU2B0EReadDeviceIdentificationRequest
    295 raw(ModbusPDU2B0EReadDeviceIdentificationRequest()) == b'+\x0e\x01\x00'
    296 
    297 = ModbusPDU2B0EReadDeviceIdentificationResponse
    298 raw(ModbusPDU2B0EReadDeviceIdentificationResponse()) == b'+\x0e\x04\x01\x00\x00\x00'
    299 = ModbusPDU2B0EReadDeviceIdentificationResponse complete response
    300 p = raw(ModbusPDU2B0EReadDeviceIdentificationResponse(objCount=2)/ModbusObjectId(id=0, value="Obj1")/ModbusObjectId(id=1, value="Obj2"))
    301 assert(p == b'+\x0e\x04\x01\x00\x00\x02\x00\x04Obj1\x01\x04Obj2')
    302 = ModbusPDU2B0EReadDeviceIdentificationResponse dissection
    303 p = ModbusPDU2B0EReadDeviceIdentificationResponse(b'+\x0e\x01\x83\x00\x00\x03\x00\x08Pymodbus\x01\x02PM\x02\x031.0')
    304 assert(p.payload.payload.payload.id == 2)
    305 assert(p.payload.payload.id == 1)
    306 assert(p.payload.id == 0)
    307 
    308 = ModbusPDU2B0EReadDeviceIdentificationError
    309 raw(ModbusPDU2B0EReadDeviceIdentificationError()) == b'\xab\x01'
    310