# coding: utf8
% ProfinetIO DCP layer test campaign

+ Syntax check
= Import the ProfinetIO layer
from scapy.contrib.pnio import *
from scapy.contrib.pnio_dcp import *
from scapy.config import conf
import re
old_conf_dissector = conf.debug_dissector
conf.debug_dissector=True

+ PNIO DCP PDU tests


= DCP Identify All Request parsing

p = Ether(b'\x01\x0e\xcf\x00\x00\x00\x01\x23\x45\x67\x89\xab\x88\x92\xfe\xfe' \
    b'\x05\x00\x01\x00\x00\x01\x00\x01\x00\x04\xff\xff\x00\x00\x00\x00' \
    b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
    b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')

assert p[Ether].dst == '01:0e:cf:00:00:00'
assert p[Ether].src == '01:23:45:67:89:ab'
assert p[Ether].type == 0x8892
assert p[ProfinetIO].frameID == 0xfefe
assert p[ProfinetDCP].service_id == 0x05
assert p[ProfinetDCP].service_type == 0x00
assert p[ProfinetDCP].xid == 0x1000001
assert p[ProfinetDCP].reserved == 0x01
assert p[ProfinetDCP].dcp_data_length == 0x04
assert p[ProfinetDCP].option == 0xff
assert p[ProfinetDCP].sub_option == 0xff
assert p[ProfinetDCP].dcp_block_length == 0x00


= DCP Set Request parsing

p = Ether(b'\x01\x23\x45\x67\x89\xac\x01\x23\x45\x67\x89\xab\x88\x92\xfe\xfd' \
       b'\x04\x00\x00\x00\x00\x01\x00\x00\x00\x0c\x02\x02\x00\x08\x00\x01' \
       b'\x64\x65\x76\x69\x63\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
       b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')

assert p[Ether].dst == '01:23:45:67:89:ac'
assert p[Ether].src == '01:23:45:67:89:ab'
assert p[Ether].type == 0x8892
assert p[ProfinetIO].frameID == 0xfefd
assert p[ProfinetDCP].service_id == 0x04
assert p[ProfinetDCP].service_type == 0x00
assert p[ProfinetDCP].xid == 0x0000001
assert p[ProfinetDCP].reserved == 0x00
assert p[ProfinetDCP].dcp_data_length == 0x0c
assert p[ProfinetDCP].option == 0x02
assert p[ProfinetDCP].sub_option == 0x02
assert p[ProfinetDCP].dcp_block_length == 0x08
assert p[ProfinetDCP].block_qualifier ==  0x01


= DCP Identify Response parsing

p = Ether(b'\x94\x65\x9c\x51\x90\x7d\xac\x64\x17\x21\x35\xcf\x81\x00\x00\x00' \
       b'\x88\x92\xfe\xff\x05\x01\x01\x00\x00\x01\x00\x00\x00\x4e\x02\x05' \
       b'\x00\x04\x00\x00\x02\x07\x02\x01\x00\x09\x00\x00\x45\x54\x32\x30' \
       b'\x30\x53\x50\x00\x02\x02\x00\x08\x00\x00\x64\x65\x76\x69\x63\x65' \
       b'\x02\x03\x00\x06\x00\x00\x00\x2a\x03\x13\x02\x04\x00\x04\x00\x00' \
       b'\x01\x00\x02\x07\x00\x04\x00\x00\x00\x01\x01\x02\x00\x0e\x00\x01' \
       b'\xc0\xa8\x01\x0e\xff\xff\xff\x00\xc0\xa8\x01\x0e')

# General
assert p[ProfinetIO].frameID == 0xfeff
assert p[ProfinetDCP].service_id == 0x05
assert p[ProfinetDCP].service_type == 0x01
assert p[ProfinetDCP].xid == 0x1000001
assert p[ProfinetDCP].reserved == 0x00
assert p[ProfinetDCP].dcp_data_length == 0x4e

# - DCPDeviceOptionsBlock
assert p[DCPDeviceOptionsBlock].option == 0x02
assert p[DCPDeviceOptionsBlock].sub_option == 0x05
assert p[DCPDeviceOptionsBlock].dcp_block_length == 0x04
assert p[DCPDeviceOptionsBlock].block_info == 0x00

# -- DeviceOption
assert p[DeviceOption].option == 0x02
assert p[DeviceOption].sub_option == 0x07

# - DCPManufacturerSpecificBlock
assert p[DCPManufacturerSpecificBlock].option == 0x02
assert p[DCPManufacturerSpecificBlock].sub_option == 0x01
assert p[DCPManufacturerSpecificBlock].dcp_block_length == 0x09
assert p[DCPManufacturerSpecificBlock].block_info == 0x00
assert p[DCPManufacturerSpecificBlock].device_vendor_value == b'ET200SP'

# - DCPNameOfStationBlock
assert p[DCPNameOfStationBlock].option == 0x02
assert p[DCPNameOfStationBlock].sub_option == 0x02
assert p[DCPNameOfStationBlock].dcp_block_length == 0x08
assert p[DCPNameOfStationBlock].block_info == 0x00
assert p[DCPNameOfStationBlock].name_of_station == b'device'

# - DCPDeviceIDBlock
assert p[DCPDeviceIDBlock].option == 0x02
assert p[DCPDeviceIDBlock].sub_option == 0x03
assert p[DCPDeviceIDBlock].dcp_block_length == 0x06
assert p[DCPDeviceIDBlock].block_info == 0x00
assert p[DCPDeviceIDBlock].vendor_id == 0x002a
assert p[DCPDeviceIDBlock].device_id == 0x0313

# - DCPDeviceRoleBlock
assert p[DCPDeviceRoleBlock].option == 0x02
assert p[DCPDeviceRoleBlock].sub_option == 0x04
assert p[DCPDeviceRoleBlock].dcp_block_length == 0x04
assert p[DCPDeviceRoleBlock].block_info == 0x00
assert p[DCPDeviceRoleBlock].device_role_details == 0x01

# - DCPDeviceInstanceBlock
assert p[DCPDeviceInstanceBlock].option == 0x02
assert p[DCPDeviceInstanceBlock].sub_option == 0x07
assert p[DCPDeviceInstanceBlock].dcp_block_length == 0x04
assert p[DCPDeviceInstanceBlock].block_info == 0x00
assert p[DCPDeviceInstanceBlock].device_instance_high == 0x00
assert p[DCPDeviceInstanceBlock].device_instance_low == 0x01

# - DCPIPBlock
assert p[DCPIPBlock].option == 0x01
assert p[DCPIPBlock].sub_option == 0x02
assert p[DCPIPBlock].dcp_block_length == 0x0e
assert p[DCPIPBlock].block_info == 0x01
assert p[DCPIPBlock].ip == "192.168.1.14"
assert p[DCPIPBlock].netmask == "255.255.255.0"
assert p[DCPIPBlock].gateway == "192.168.1.14"

= DCP Identify Response parsing with new DCP packages (DCPOEMIDBlock, DCPDeviceInitiativeBlock)

p = Ether(b'\x01\x0e\xcf\x00\x00\x00\x01\x23\x45\x67\x89\xab\x88\x92' \
    b'\xfe\xff\x05\x01\x01\x00\x00\x01\x00\x00\x00\x7a\x02\x02\x00\x02\x00' \
    b'\x00\x01\x02\x00\x0e\x00\x01\xc0\xa8\x01\x0b\xff\xff\xff\x00\x00\x00' \
    b'\x00\x00\x02\x03\x00\x06\x00\x00\x01\x6a\x04\x00\x02\x05\x00\x16\x00' \
    b'\x00\x01\x01\x01\x02\x02\x01\x02\x02\x02\x03\x02\x04\x02\x05\x02\x07' \
    b'\x02\x08\x06\x01\x02\x04\x00\x04\x00\x00\x01\x00\x06\x01\x00\x04\x00' \
    b'\x00\x00\x00\x02\x01\x00\x18\x00\x00\x31\x32\x33\x34\x20\x44\x44\x44' 
    b'\x20\x33\x58\x58\x32\x2d\x31\x32\x31\x2d\x30\x46\x44\x44\x02\x07\x00' \
    b'\x04\x00\x00\x00\x01\x02\x08\x00\x06\x00\x00\x01\x1e\xff\xff')

# - General
assert p[Ether].dst == '01:0e:cf:00:00:00'
assert p[Ether].src == '01:23:45:67:89:ab'
assert p[Ether].type == 0x8892
assert p[ProfinetIO].frameID == 0xFEFF
assert p[ProfinetDCP].service_id == 0x05
assert p[ProfinetDCP].service_type == 0x01
assert p[ProfinetDCP].xid == 0x1000001
assert p[ProfinetDCP].reserved == 0x00
assert p[ProfinetDCP].dcp_data_length == 122
assert list(map(lambda x: type(x), p[ProfinetDCP].dcp_blocks)) == [DCPNameOfStationBlock, DCPIPBlock, DCPDeviceIDBlock, DCPDeviceOptionsBlock, DCPDeviceRoleBlock, DCPDeviceInitiativeBlock, DCPManufacturerSpecificBlock, DCPDeviceInstanceBlock, DCPOEMIDBlock]

# - DCPNameOfStationBlock
assert p[DCPNameOfStationBlock].option == 0x02
assert p[DCPNameOfStationBlock].sub_option == 0x02

# - DCPIPBlock
assert p[DCPIPBlock].option == 0x01
assert p[DCPIPBlock].sub_option == 0x02
assert p[DCPIPBlock].dcp_block_length == 0x0E
assert p[DCPIPBlock].ip == '192.168.1.11'
assert p[DCPIPBlock].netmask == '255.255.255.0'
assert p[DCPIPBlock].gateway == '0.0.0.0'

# - DCPDeviceInitiativeBlock
assert p[DCPDeviceInitiativeBlock].option == 0x06
assert p[DCPDeviceInitiativeBlock].sub_option == 0x01
assert p[DCPDeviceInitiativeBlock].dcp_block_length == 0x04
assert p[DCPDeviceInitiativeBlock].device_initiative == 0x0000

# - DCPManufacturerSpecificBlock
assert p[DCPManufacturerSpecificBlock].option == 0x02
assert p[DCPManufacturerSpecificBlock].sub_option == 0x01
assert p[DCPManufacturerSpecificBlock].device_vendor_value == b'1234 DDD 3XX2-121-0FDD'

# - DCPOEMIDBlock
assert p[DCPOEMIDBlock].option == 0x02
assert p[DCPOEMIDBlock].sub_option == 0x08
assert p[DCPOEMIDBlock].dcp_block_length == 0x06
assert p[DCPOEMIDBlock].vendor_id == 0x011e
assert p[DCPOEMIDBlock].device_id == 0xffff

= DCP Set Request parsing

p = Ether(b'\x94\x65\x9c\x51\x90\x7d\xac\x64\x17\x21\x35\xcf\x81\x00\x00\x00' \
      b'\x88\x92\xfe\xfd\x04\x01\x00\x00\x00\x01\x00\x00\x00\x08\x05\x04' \
      b'\x00\x03\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \
      b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')

assert p[ProfinetIO].frameID == 0xfefd
assert p[ProfinetDCP].service_id == 0x04
assert p[ProfinetDCP].service_type == 0x01
assert p[ProfinetDCP].xid == 0x0000001
assert p[ProfinetDCP].reserved == 0x00
assert p[ProfinetDCP].dcp_data_length == 0x08

assert p[DCPControlBlock].option == 0x05
assert p[DCPControlBlock].sub_option == 0x04
assert p[DCPControlBlock].dcp_block_length == 0x03
assert p[DCPControlBlock].response == 0x02
assert p[DCPControlBlock].response_sub_option == 0x02
assert p[DCPControlBlock].block_error == 0x00


= DCP Set Full IP Suite Request parsing

p = Ether(b'\x12\x34\x00\x78\x90\xab\xc8\x5b\x76\xe6\x89\xdf' \
      b'\x88\x92\xfe\xfd\x04\x00\x00\x00\x00\x04\x00\x00' \
      b'\x00\x28\x01\x03\x00\x1e\x00\x00\xc0\xa8\x01\xab' \
      b'\xff\xff\xff\x00\xc0\xa8\x01\x01\x01\x02\x03\x04' \
      b'\x05\x06\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00')

assert p[ProfinetIO].frameID == 0xfefd
assert p[ProfinetDCP].service_id == 0x04
assert p[ProfinetDCP].service_type == 0x00
assert p[ProfinetDCP].xid == 0x0000004
assert p[ProfinetDCP].reserved == 0x00
assert p[ProfinetDCP].dcp_data_length == 40
assert p[ProfinetDCP].option == 0x01
assert p[ProfinetDCP].sub_option == 0x03
assert p[ProfinetDCP].ip == "192.168.1.171"
assert p[ProfinetDCP].netmask == "255.255.255.0"
assert p[ProfinetDCP].gateway == "192.168.1.1"
assert p[ProfinetDCP].dnsaddr[0] == "1.2.3.4"
assert p[ProfinetDCP].dnsaddr[1] == "5.6.7.8"
assert p[ProfinetDCP].dnsaddr[2] == "0.0.0.0"
assert p[ProfinetDCP].dnsaddr[3] == "0.0.0.0"


= DCP Identify All Request crafting

# dcp_data_length cannot be calculated automatically at this time
p = ProfinetIO(frameID=DCP_IDENTIFY_REQUEST_FRAME_ID) / ProfinetDCP(service_id=DCP_SERVICE_ID_IDENTIFY, service_type=DCP_REQUEST, option=255, sub_option=255, dcp_data_length=4)

assert p[ProfinetIO].frameID == 0xfefe
assert p[ProfinetDCP].service_id == 0x05
assert p[ProfinetDCP].service_type == 0x00
assert p[ProfinetDCP].xid == 0x1000001
assert p[ProfinetDCP].reserved == 0x00
assert p[ProfinetDCP].dcp_data_length == 0x04
assert p[ProfinetDCP].option == 0xff
assert p[ProfinetDCP].sub_option == 0xff
assert p[ProfinetDCP].dcp_block_length == 0x00


= DCP Set Name Request with specified name crafting

p = ProfinetIO(frameID=DCP_GET_SET_FRAME_ID) / ProfinetDCP ( service_id=DCP_SERVICE_ID_SET, service_type=DCP_REQUEST, option=2, sub_option=2, name_of_station="device", dcp_block_length=8, dcp_data_length=12)

assert p[ProfinetIO].frameID == 0xfefd
assert p[ProfinetDCP].service_id == 0x04
assert p[ProfinetDCP].service_type == 0x00
assert p[ProfinetDCP].xid == 0x1000001
assert p[ProfinetDCP].reserved == 0x00
assert p[ProfinetDCP].dcp_data_length == 0x0c
assert p[ProfinetDCP].option == 0x02
assert p[ProfinetDCP].sub_option == 0x02
assert p[ProfinetDCP].dcp_block_length == 0x08
assert p[ProfinetDCP].block_qualifier == 0x0001


= DCP Identify Response crafting

p = ProfinetIO(frameID=DCP_IDENTIFY_RESPONSE_FRAME_ID) / ProfinetDCP(service_id=DCP_SERVICE_ID_IDENTIFY, service_type=DCP_RESPONSE, dcp_data_length=12) / DCPNameOfStationBlock(name_of_station="device", dcp_block_length=8)

assert p[ProfinetIO].frameID == 0xfeff
assert p[ProfinetDCP].service_id == 0x05
assert p[ProfinetDCP].service_type == 0x01
assert p[ProfinetDCP].xid == 0x1000001
assert p[ProfinetDCP].reserved == 0x00
assert p[ProfinetDCP].dcp_data_length == 0x0c
assert p[DCPNameOfStationBlock].option == 0x02
assert p[DCPNameOfStationBlock].sub_option == 0x02
assert p[DCPNameOfStationBlock].dcp_block_length == 0x08
assert p[DCPNameOfStationBlock].block_info == 0x00
assert p[DCPNameOfStationBlock].name_of_station == b'device'


= DCP Set Full IP Suite Request crafting

p = ProfinetIO(frameID=DCP_GET_SET_FRAME_ID) / ProfinetDCP(service_id=DCP_SERVICE_ID_SET, service_type=DCP_REQUEST, option=1, sub_option=3, ip='192.168.1.171', netmask='255.255.255.0', gateway='192.168.1.1', dnsaddr=['1.2.3.4', '5.6.7.8'], dcp_data_length=40, dcp_block_length=30)

assert p[ProfinetIO].frameID == 0xfefd
assert p[ProfinetDCP].service_id == 0x04
assert p[ProfinetDCP].service_type == 0x00
assert p[ProfinetDCP].xid == 0x1000001
assert p[ProfinetDCP].reserved == 0x00
assert p[ProfinetDCP].dcp_data_length == 40
assert p[ProfinetDCP].option == 0x01
assert p[ProfinetDCP].sub_option == 0x03
assert p[ProfinetDCP].ip == "192.168.1.171"
assert p[ProfinetDCP].netmask == "255.255.255.0"
assert p[ProfinetDCP].gateway == "192.168.1.1"
assert p[ProfinetDCP].dnsaddr[0] == "1.2.3.4"
assert p[ProfinetDCP].dnsaddr[1] == "5.6.7.8"


conf.debug_dissector = old_conf_dissector
