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