| % Modbus layer test campaign |
| |
| + Syntax check |
| = Import the modbus layer |
| from scapy.contrib.modbus import * |
| |
| + Test MBAP |
| = MBAP default values |
| raw(ModbusADURequest()) == b'\x00\x00\x00\x00\x00\x01\xff' |
| |
| = MBAP payload length calculation |
| raw(ModbusADURequest() / b'\x00\x01\x02') == b'\x00\x00\x00\x00\x00\x04\xff\x00\x01\x02' |
| |
| = MBAP Guess Payload ModbusPDU01ReadCoilsRequest (simple case) |
| p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x06\xff\x01\x00\x00\x00\x01') |
| assert(isinstance(p.payload, ModbusPDU01ReadCoilsRequest)) |
| = MBAP Guess Payload ModbusPDU01ReadCoilsResponse |
| p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x04\xff\x01\x01\x01') |
| assert(isinstance(p.payload, ModbusPDU01ReadCoilsResponse)) |
| = MBAP Guess Payload ModbusPDU01ReadCoilsError |
| p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x03\xff\x81\x02') |
| assert(isinstance(p.payload, ModbusPDU01ReadCoilsError)) |
| |
| = MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationRequest (2 level test) |
| p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x04\xff+\x0e\x01\x00') |
| assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationRequest)) |
| = MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationResponse |
| p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x1b\xff+\x0e\x01\x83\x00\x00\x03\x00\x08Pymodbus\x01\x02PM\x02\x031.0') |
| assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationResponse)) |
| = MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationError |
| p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x03\xff\xab\x01') |
| assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationError)) |
| |
| = MBAP Guess Payload Reserved Function Request (Invalid payload) |
| p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x02\xff\x5b') |
| assert(isinstance(p.payload,ModbusPDUReservedFunctionCodeRequest)) |
| = MBAP Guess Payload Reserved Function Response (Invalid payload) |
| p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x7e') |
| assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeResponse)) |
| = MBAP Guess Payload Reserved Function Error (Invalid payload) |
| p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x8a') |
| assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeError)) |
| |
| = MBAP Guess Payload ModbusPDU02ReadDiscreteInputsResponse |
| assert(raw(ModbusPDU02ReadDiscreteInputsResponse()), b'\x02\x01\x00') |
| = MBAP Guess Payload ModbusPDU02ReadDiscreteInputsResponse minimal parameters |
| assert(raw(ModbusPDU02ReadDiscreteInputsResponse(inputStatus=[0x02, 0x01])), b'\x02\x02\x02\x01') |
| = MBAP Guess Payload ModbusPDU02ReadDiscreteInputsRequest dissection |
| p = ModbusPDU02ReadDiscreteInputsResponse(b'\x02\x02\x02\x01') |
| p.byteCount == 2 and p.inputStatus == [0x02, 0x01] |
| |
| = ModbusPDU02ReadDiscreteInputsError |
| raw(ModbusPDU02ReadDiscreteInputsError()) == b'\x82\x01' |
| |
| = MBAP Guess Payload User-Defined Function Request (Invalid payload) |
| p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x02\xff\x5b') |
| assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeRequest)) |
| = MBAP Guess Payload User-Defined Function Response (Invalid payload) |
| p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x7e') |
| assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeResponse)) |
| = MBAP Guess Payload User-Defined Function Error (Invalid payload) |
| p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x8a') |
| assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeError)) |
| |
| + Test layer binding |
| = Destination port |
| p = TCP()/ModbusADURequest() |
| p[TCP].dport == 502 |
| |
| = Source port |
| p = TCP()/ModbusADUResponse() |
| p[TCP].sport == 502 |
| |
| + Test PDU |
| * Note on tests cases: dissection/minimal parameters will not be done for packets that does not perform calculation |
| # 0x01/0x81 Read Coils -------------------------------------------------------------- |
| = ModbusPDU01ReadCoilsRequest |
| raw(ModbusPDU01ReadCoilsRequest()) == b'\x01\x00\x00\x00\x01' |
| = ModbusPDU01ReadCoilsRequest minimal parameters |
| raw(ModbusPDU01ReadCoilsRequest(startAddr=16, quantity=2)) == b'\x01\x00\x10\x00\x02' |
| = ModbusPDU01ReadCoilsRequest dissection |
| p = ModbusPDU01ReadCoilsRequest(b'\x01\x00\x10\x00\x02') |
| assert(p.startAddr == 16) |
| assert(p.quantity == 2) |
| |
| = ModbusPDU01ReadCoilsResponse |
| raw(ModbusPDU01ReadCoilsResponse()) == b'\x01\x01\x00' |
| = ModbusPDU01ReadCoilsResponse minimal parameters |
| raw(ModbusPDU01ReadCoilsResponse(coilStatus=[0x10]*3)) == b'\x01\x03\x10\x10\x10' |
| = ModbusPDU01ReadCoilsResponse dissection |
| p = ModbusPDU01ReadCoilsResponse(b'\x01\x03\x10\x10\x10') |
| assert(p.coilStatus == [16, 16, 16]) |
| assert(p.byteCount == 3) |
| |
| = ModbusPDU01ReadCoilsError |
| raw(ModbusPDU01ReadCoilsError()) == b'\x81\x01' |
| = ModbusPDU81ReadCoilsError minimal parameters |
| raw(ModbusPDU01ReadCoilsError(exceptCode=2)) == b'\x81\x02' |
| = ModbusPDU81ReadCoilsError dissection |
| p = ModbusPDU01ReadCoilsError(b'\x81\x02') |
| assert(p.funcCode == 0x81) |
| assert(p.exceptCode == 2) |
| |
| # 0x02/0x82 Read Discrete Inputs Registers ------------------------------------------ |
| = ModbusPDU02ReadDiscreteInputsRequest |
| raw(ModbusPDU02ReadDiscreteInputsRequest()) == b'\x02\x00\x00\x00\x01' |
| = ModbusPDU02ReadDiscreteInputsRequest minimal parameters |
| raw(ModbusPDU02ReadDiscreteInputsRequest(startAddr=8, quantity=128)) == b'\x02\x00\x08\x00\x80' |
| |
| = ModbusPDU02ReadDiscreteInputsResponse |
| raw(ModbusPDU02ReadDiscreteInputsResponse()) == b'\x02\x01\x00' |
| = ModbusPDU02ReadDiscreteInputsResponse minimal parameters |
| raw(ModbusPDU02ReadDiscreteInputsResponse(inputStatus=[0x02, 0x01])) == b'\x02\x02\x02\x01' |
| = ModbusPDU02ReadDiscreteInputsRequest dissection |
| p = ModbusPDU02ReadDiscreteInputsResponse(b'\x02\x02\x02\x01') |
| assert(p.byteCount == 2) |
| assert(p.inputStatus == [0x02, 0x01]) |
| |
| = ModbusPDU02ReadDiscreteInputsError |
| raw(ModbusPDU02ReadDiscreteInputsError()) == b'\x82\x01' |
| |
| # 0x03/0x83 Read Holding Registers -------------------------------------------------- |
| = ModbusPDU03ReadHoldingRegistersRequest |
| raw(ModbusPDU03ReadHoldingRegistersRequest()) == b'\x03\x00\x00\x00\x01' |
| = ModbusPDU03ReadHoldingRegistersRequest minimal parameters |
| raw(ModbusPDU03ReadHoldingRegistersRequest(startAddr=2048, quantity=16)) == b'\x03\x08\x00\x00\x10' |
| |
| = ModbusPDU03ReadHoldingRegistersResponse |
| raw(ModbusPDU03ReadHoldingRegistersResponse()) == b'\x03\x02\x00\x00' |
| = ModbusPDU03ReadHoldingRegistersResponse minimal parameters |
| 1==1 |
| = ModbusPDU03ReadHoldingRegistersResponse dissection |
| p = ModbusPDU03ReadHoldingRegistersResponse(b'\x03\x06\x02+\x00\x00\x00d') |
| assert(p.byteCount == 6) |
| assert(p.registerVal == [555, 0, 100]) |
| |
| = ModbusPDU03ReadHoldingRegistersError |
| raw(ModbusPDU03ReadHoldingRegistersError()) == b'\x83\x01' |
| |
| # 0x04/0x84 Read Input Register ----------------------------------------------------- |
| = ModbusPDU04ReadInputRegistersRequest |
| raw(ModbusPDU04ReadInputRegistersRequest()) == b'\x04\x00\x00\x00\x01' |
| |
| = ModbusPDU04ReadInputRegistersResponse |
| raw(ModbusPDU04ReadInputRegistersResponse()) == b'\x04\x02\x00\x00' |
| = ModbusPDU04ReadInputRegistersResponse minimal parameters |
| raw(ModbusPDU04ReadInputRegistersResponse(registerVal=[0x01, 0x02])) == b'\x04\x04\x00\x01\x00\x02' |
| |
| = ModbusPDU04ReadInputRegistersError |
| raw(ModbusPDU04ReadInputRegistersError()) == b'\x84\x01' |
| |
| # 0x05/0x85 Write Single Coil ------------------------------------------------------- |
| = ModbusPDU05WriteSingleCoilRequest |
| raw(ModbusPDU05WriteSingleCoilRequest()) == b'\x05\x00\x00\x00\x00' |
| |
| = ModbusPDU05WriteSingleCoilResponse |
| raw(ModbusPDU05WriteSingleCoilResponse()) == b'\x05\x00\x00\x00\x00' |
| |
| = ModbusPDU05WriteSingleCoilError |
| raw(ModbusPDU05WriteSingleCoilError()) == b'\x85\x01' |
| |
| # 0x06/0x86 Write Single Register --------------------------------------------------- |
| = ModbusPDU06WriteSingleRegisterError |
| raw(ModbusPDU06WriteSingleRegisterRequest()) == b'\x06\x00\x00\x00\x00' |
| |
| = ModbusPDU06WriteSingleRegisterResponse |
| raw(ModbusPDU06WriteSingleRegisterResponse()) == b'\x06\x00\x00\x00\x00' |
| |
| = ModbusPDU06WriteSingleRegisterError |
| raw(ModbusPDU06WriteSingleRegisterError()) == b'\x86\x01' |
| |
| # 0x07/0x87 Read Exception Status (serial line only) -------------------------------- |
| # 0x08/0x88 Diagnostics (serial line only) ------------------------------------------ |
| # 0x0b Get Comm Event Counter: serial line only ------------------------------------- |
| # 0x0c Get Comm Event Log: serial line only ----------------------------------------- |
| |
| # 0x0f/0x8f Write Multiple Coils ---------------------------------------------------- |
| = ModbusPDU0FWriteMultipleCoilsRequest |
| raw(ModbusPDU0FWriteMultipleCoilsRequest()) |
| = ModbusPDU0FWriteMultipleCoilsRequest minimal parameters |
| raw(ModbusPDU0FWriteMultipleCoilsRequest(outputsValue=[0x01, 0x01])) == b'\x0f\x00\x00\x00\x01\x02\x01\x01' |
| |
| = ModbusPDU0FWriteMultipleCoilsResponse |
| raw(ModbusPDU0FWriteMultipleCoilsResponse()) == b'\x0f\x00\x00\x00\x01' |
| |
| = ModbusPDU0FWriteMultipleCoilsError |
| raw(ModbusPDU0FWriteMultipleCoilsError()) == b'\x8f\x01' |
| |
| # 0x10/0x90 Write Multiple Registers ---------------------------------------------------- |
| = ModbusPDU10WriteMultipleRegistersRequest |
| raw(ModbusPDU10WriteMultipleRegistersRequest()) == b'\x10\x00\x00\x00\x01\x02\x00\x00' |
| = ModbusPDU10WriteMultipleRegistersRequest minimal parameters |
| raw(ModbusPDU10WriteMultipleRegistersRequest(outputsValue=[0x0001, 0x0002])) == b'\x10\x00\x00\x00\x02\x04\x00\x01\x00\x02' |
| |
| = ModbusPDU10WriteMultipleRegistersResponse |
| raw(ModbusPDU10WriteMultipleRegistersResponse()) == b'\x10\x00\x00\x00\x01' |
| |
| = ModbusPDU10WriteMultipleRegistersError |
| raw(ModbusPDU10WriteMultipleRegistersError()) == b'\x90\x01' |
| |
| # 0x11/91 Report Server ID: serial line only ---------------------------------------- |
| |
| # 0x14/944 Read File Record --------------------------------------------------------- |
| = ModbusPDU14ReadFileRecordRequest len parameters |
| p = raw(ModbusPDU14ReadFileRecordRequest()/ModbusReadFileSubRequest()/ModbusReadFileSubRequest()) |
| assert(p == b'\x14\x0e\x06\x00\x01\x00\x00\x00\x01\x06\x00\x01\x00\x00\x00\x01') |
| = ModbusPDU14ReadFileRecordRequest minimal parameters |
| p = raw(ModbusPDU14ReadFileRecordRequest()/ModbusReadFileSubRequest(fileNumber=4, recordNumber=1, recordLength=2)/ModbusReadFileSubRequest(fileNumber=3, recordNumber=9, recordLength=2)) |
| assert(p == b'\x14\x0e\x06\x00\x04\x00\x01\x00\x02\x06\x00\x03\x00\t\x00\x02') |
| = ModbusPDU14ReadFileRecordRequest dissection |
| p = ModbusPDU14ReadFileRecordRequest(b'\x14\x0e\x06\x00\x04\x00\x01\x00\x02\x06\x00\x03\x00\t\x00\x02') |
| assert(isinstance(p.payload, ModbusReadFileSubRequest)) |
| assert(isinstance(p.payload.payload, ModbusReadFileSubRequest)) |
| |
| = ModbusPDU14ReadFileRecordResponse minimal parameters |
| raw(ModbusPDU14ReadFileRecordResponse()/ModbusReadFileSubResponse(recData=[0x0dfe, 0x0020])/ModbusReadFileSubResponse(recData=[0x33cd, 0x0040])) == b'\x14\x0c\x05\x06\r\xfe\x00 \x05\x063\xcd\x00@' |
| = ModbusPDU14ReadFileRecordResponse dissection |
| p = ModbusPDU14ReadFileRecordResponse(b'\x14\x0c\x05\x06\r\xfe\x00 \x05\x063\xcd\x00@') |
| assert(isinstance(p.payload, ModbusReadFileSubResponse)) |
| assert(isinstance(p.payload.payload, ModbusReadFileSubResponse)) |
| |
| = ModbusPDU14ReadFileRecordError |
| raw(ModbusPDU14ReadFileRecordError()) == b'\x94\x01' |
| |
| # 0x15/0x95 Write File Record ------------------------------------------------------- |
| = ModbusPDU15WriteFileRecordRequest minimal parameters |
| 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' |
| = ModbusPDU15WriteFileRecordRequest dissection |
| p = ModbusPDU15WriteFileRecordRequest(b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r') |
| assert(isinstance(p.payload, ModbusWriteFileSubRequest)) |
| assert(p.payload.recordLength == 3) |
| |
| = ModbusPDU15WriteFileRecordResponse minimal parameters |
| 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' |
| = ModbusPDU15WriteFileRecordResponse dissection |
| p = ModbusPDU15WriteFileRecordResponse(b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r') |
| assert(isinstance(p.payload, ModbusWriteFileSubResponse)) |
| assert(p.payload.recordLength == 3) |
| |
| = ModbusPDU15WriteFileRecordError |
| raw(ModbusPDU15WriteFileRecordError()) == b'\x95\x01' |
| |
| # 0x16/0x96 Mask Write Register ----------------------------------------------------- |
| = ModbusPDU16MaskWriteRegisterRequest |
| raw(ModbusPDU16MaskWriteRegisterRequest()) == b'\x16\x00\x00\xff\xff\x00\x00' |
| |
| = ModbusPDU16MaskWriteRegisterResponse |
| raw(ModbusPDU16MaskWriteRegisterResponse()) == b'\x16\x00\x00\xff\xff\x00\x00' |
| |
| = ModbusPDU16MaskWriteRegisterError |
| raw(ModbusPDU16MaskWriteRegisterError()) == b'\x96\x01' |
| |
| # 0x17/0x97 Read/Write Multiple Registers ------------------------------------------- |
| = ModbusPDU17ReadWriteMultipleRegistersRequest |
| raw(ModbusPDU17ReadWriteMultipleRegistersRequest()) == b'\x17\x00\x00\x00\x01\x00\x00\x00\x01\x02\x00\x00' |
| = ModbusPDU17ReadWriteMultipleRegistersRequest minimal parameters |
| raw(ModbusPDU17ReadWriteMultipleRegistersRequest(writeRegistersValue=[0x0001, 0x0002])) == b'\x17\x00\x00\x00\x01\x00\x00\x00\x02\x04\x00\x01\x00\x02' |
| = ModbusPDU17ReadWriteMultipleRegistersRequest dissection |
| p = ModbusPDU17ReadWriteMultipleRegistersRequest(b'\x17\x00\x00\x00\x01\x00\x00\x00\x02\x04\x00\x01\x00\x02') |
| assert(p.byteCount == 4) |
| assert(p.writeQuantityRegisters == 2) |
| |
| = ModbusPDU17ReadWriteMultipleRegistersResponse |
| raw(ModbusPDU17ReadWriteMultipleRegistersResponse()) == b'\x17\x02\x00\x00' |
| = ModbusPDU17ReadWriteMultipleRegistersResponse minimal parameters |
| raw(ModbusPDU17ReadWriteMultipleRegistersResponse(registerVal=[1,2,3])) == b'\x17\x06\x00\x01\x00\x02\x00\x03' |
| = ModbusPDU17ReadWriteMultipleRegistersResponse dissection |
| raw(ModbusPDU17ReadWriteMultipleRegistersResponse(b'\x17\x02\x00\x01')) == b'\x17\x02\x00\x01' |
| |
| = ModbusPDU17ReadWriteMultipleRegistersError |
| raw(ModbusPDU17ReadWriteMultipleRegistersError()) == b'\x97\x01' |
| |
| # 0x18/0x88 Read FIFO Queue --------------------------------------------------------- |
| = ModbusPDU18ReadFIFOQueueRequest |
| raw(ModbusPDU18ReadFIFOQueueRequest()) == b'\x18\x00\x00' |
| |
| = ModbusPDU18ReadFIFOQueueResponse |
| = ModbusPDU18ReadFIFOQueueResponse |
| raw(ModbusPDU18ReadFIFOQueueResponse()) == b'\x18\x00\x02\x00\x00' |
| = ModbusPDU18ReadFIFOQueueResponse minimal parameters |
| raw(ModbusPDU18ReadFIFOQueueResponse(FIFOVal=[0x0001, 0x0002, 0x0003])) == b'\x18\x00\x08\x00\x03\x00\x01\x00\x02\x00\x03' |
| = ModbusPDU18ReadFIFOQueueResponse dissection |
| p = ModbusPDU18ReadFIFOQueueResponse(b'\x18\x00\x08\x00\x03\x00\x01\x00\x02\x00\x03') |
| assert(p.byteCount == 8) |
| assert(p.FIFOCount == 3) |
| |
| = ModbusPDU18ReadFIFOQueueError |
| raw(ModbusPDU18ReadFIFOQueueError()) == b'\x98\x01' |
| |
| # 0x2b encapsulated Interface Transport --------------------------------------------- |
| # 0x2b 0xOD CANopen General Reference (out of the main specification) --------------- |
| |
| # 0x2b 0xOE Read Device Information ------------------------------------------------- |
| = ModbusPDU2B0EReadDeviceIdentificationRequest |
| raw(ModbusPDU2B0EReadDeviceIdentificationRequest()) == b'+\x0e\x01\x00' |
| |
| = ModbusPDU2B0EReadDeviceIdentificationResponse |
| raw(ModbusPDU2B0EReadDeviceIdentificationResponse()) == b'+\x0e\x04\x01\x00\x00\x00' |
| = ModbusPDU2B0EReadDeviceIdentificationResponse complete response |
| p = raw(ModbusPDU2B0EReadDeviceIdentificationResponse(objCount=2)/ModbusObjectId(id=0, value="Obj1")/ModbusObjectId(id=1, value="Obj2")) |
| assert(p == b'+\x0e\x04\x01\x00\x00\x02\x00\x04Obj1\x01\x04Obj2') |
| = ModbusPDU2B0EReadDeviceIdentificationResponse dissection |
| p = ModbusPDU2B0EReadDeviceIdentificationResponse(b'+\x0e\x01\x83\x00\x00\x03\x00\x08Pymodbus\x01\x02PM\x02\x031.0') |
| assert(p.payload.payload.payload.id == 2) |
| assert(p.payload.payload.id == 1) |
| assert(p.payload.id == 0) |
| |
| = ModbusPDU2B0EReadDeviceIdentificationError |
| raw(ModbusPDU2B0EReadDeviceIdentificationError()) == b'\xab\x01' |