229 lines
7.6 KiB
Text
229 lines
7.6 KiB
Text
|
# coding: utf8
|
||
|
% ProfinetIO layer test campaign
|
||
|
|
||
|
+ Syntax check
|
||
|
= Import the ProfinetIO layer
|
||
|
from scapy.contrib.pnio import *
|
||
|
from scapy.config import conf
|
||
|
import re
|
||
|
old_conf_dissector = conf.debug_dissector
|
||
|
conf.debug_dissector=True
|
||
|
|
||
|
|
||
|
+ Check DCE/RPC layer
|
||
|
|
||
|
= ProfinetIO default values
|
||
|
raw(ProfinetIO()) == b'\x00\x00'
|
||
|
|
||
|
= ProfinetIO overloads Ethertype
|
||
|
p = Ether() / ProfinetIO()
|
||
|
p.type == 0x8892
|
||
|
|
||
|
= ProfinetIO overloads UDP dport
|
||
|
p = UDP() / ProfinetIO()
|
||
|
p.dport == 0x8892
|
||
|
|
||
|
= Ether guesses ProfinetIO as payload class
|
||
|
p = Ether(hex_bytes('ffffffffffff00000000000088920102'))
|
||
|
isinstance(p.payload, ProfinetIO) and p.frameID == 0x0102
|
||
|
|
||
|
= UDP guesses ProfinetIO as payload class
|
||
|
p = UDP(hex_bytes('12348892000a00000102'))
|
||
|
isinstance(p.payload, ProfinetIO) and p.frameID == 0x0102
|
||
|
|
||
|
|
||
|
+ PNIO RTC PDU tests
|
||
|
|
||
|
= ProfinetIO PNIORealTime_IOxS parsing of a single status
|
||
|
|
||
|
p = PNIORealTime_IOxS(b'\x80')
|
||
|
assert(p.dataState == 1)
|
||
|
assert(p.instance == 0)
|
||
|
assert(p.reserved == 0)
|
||
|
assert(p.extension == 0)
|
||
|
|
||
|
p = PNIORealTime_IOxS(b'\xe1')
|
||
|
assert(p.dataState == 1)
|
||
|
assert(p.instance == 3)
|
||
|
assert(p.reserved == 0)
|
||
|
assert(p.extension == 1)
|
||
|
True
|
||
|
|
||
|
= ProfinetIO PNIORealTime_IOxS building of a single status
|
||
|
p = PNIORealTime_IOxS(dataState = 'good', instance='subslot', extension=0)
|
||
|
assert(raw(p) == b'\x80')
|
||
|
|
||
|
p = PNIORealTime_IOxS(dataState = 'bad', instance='device', extension=1)
|
||
|
assert(raw(p) == b'\x41')
|
||
|
True
|
||
|
|
||
|
= ProfinetIO PNIORealTime_IOxS parsing with multiple statuses
|
||
|
TestPacket = type(
|
||
|
'TestPacket',
|
||
|
(Packet,),
|
||
|
{
|
||
|
'name': 'TestPacket',
|
||
|
'fields_desc': [
|
||
|
PacketListField('data', [], next_cls_cb= PNIORealTime_IOxS.is_extension_set)
|
||
|
],
|
||
|
}
|
||
|
)
|
||
|
|
||
|
p = TestPacket(b'\x81\xe1\x01\x80')
|
||
|
assert(len(p.data) == 4)
|
||
|
assert(p.data[0].dataState == 1)
|
||
|
assert(p.data[0].instance == 0)
|
||
|
assert(p.data[0].reserved == 0)
|
||
|
assert(p.data[0].extension == 1)
|
||
|
assert(p.data[1].dataState == 1)
|
||
|
assert(p.data[1].instance == 3)
|
||
|
assert(p.data[1].reserved == 0)
|
||
|
assert(p.data[1].extension == 1)
|
||
|
assert(p.data[2].dataState == 0)
|
||
|
assert(p.data[2].instance == 0)
|
||
|
assert(p.data[2].reserved == 0)
|
||
|
assert(p.data[2].extension == 1)
|
||
|
assert(p.data[3].dataState == 1)
|
||
|
assert(p.data[3].instance == 0)
|
||
|
assert(p.data[3].reserved == 0)
|
||
|
assert(p.data[3].extension == 0)
|
||
|
|
||
|
= ProfinetIO RTC PDU parsing without configuration
|
||
|
p = Ether(b'\x00\x02\x04\x06\x08\x0a\x01\x03\x05\x07\x09\x0B\x88\x92\x80\x00\x01\x02\x03\x04\xf0\x00\x35\x00')
|
||
|
assert(p[Ether].dst == '00:02:04:06:08:0a')
|
||
|
assert(p[Ether].src == '01:03:05:07:09:0b')
|
||
|
assert(p[Ether].type == 0x8892)
|
||
|
assert(p[ProfinetIO].frameID == 0x8000)
|
||
|
assert(isinstance(p[ProfinetIO].payload, PNIORealTimeCyclicPDU))
|
||
|
assert(len(p[PNIORealTimeCyclicPDU].data) == 1)
|
||
|
assert(isinstance(p[PNIORealTimeCyclicPDU].data[0], PNIORealTimeCyclicDefaultRawData))
|
||
|
assert(p[PNIORealTimeCyclicDefaultRawData].data == b'\x01\x02\x03\x04')
|
||
|
assert(p[PNIORealTimeCyclicPDU].padding == b'')
|
||
|
assert(p[PNIORealTimeCyclicPDU].cycleCounter == 0xf000)
|
||
|
assert(p[PNIORealTimeCyclicPDU].dataStatus == 0x35)
|
||
|
assert(p[PNIORealTimeCyclicPDU].transferStatus == 0)
|
||
|
True
|
||
|
|
||
|
= ProfinetIO RTC PDU building
|
||
|
p = Ether(src='01:03:05:07:09:0b', dst='00:02:04:06:08:0a')/ProfinetIO(frameID = 'PTCP-RTSyncPDU')/PNIORealTimeCyclicPDU(
|
||
|
data=[
|
||
|
PNIORealTimeCyclicPDU.build_fixed_len_raw_type(10)(data = b'\x80'*10)
|
||
|
],
|
||
|
padding = b'\x00'*8,
|
||
|
cycleCounter = 900,
|
||
|
dataStatus = 0x35,
|
||
|
transferStatus = 0
|
||
|
)
|
||
|
|
||
|
assert(
|
||
|
raw(p) == \
|
||
|
b'\x00\x02\x04\x06\x08\x0a' \
|
||
|
b'\x01\x03\x05\x07\x09\x0b' \
|
||
|
b'\x88\x92' \
|
||
|
b'\x00\x80' \
|
||
|
b'\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80' \
|
||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00' \
|
||
|
b'\x03\x84' \
|
||
|
b'\x35' \
|
||
|
b'\x00'
|
||
|
)
|
||
|
|
||
|
= ProfinetIO RTC PDU parsing with config
|
||
|
|
||
|
scapy.config.conf.contribs['PNIO_RTC'][('01:03:05:07:09:0b', '00:02:04:06:08:0a', 0x8010)] = [
|
||
|
PNIORealTimeCyclicPDU.build_fixed_len_raw_type(5),
|
||
|
PNIORealTimeCyclicPDU.build_fixed_len_raw_type(3),
|
||
|
PNIORealTimeCyclicPDU.build_fixed_len_raw_type(2)
|
||
|
]
|
||
|
p = Ether(
|
||
|
b'\x00\x02\x04\x06\x08\x0a' \
|
||
|
b'\x01\x03\x05\x07\x09\x0B' \
|
||
|
b'\x88\x92' \
|
||
|
b'\x80\x10' \
|
||
|
b'\x01\x02\x03\x04\x05' \
|
||
|
b'\x01\x02\x03' \
|
||
|
b'\x01\x02' \
|
||
|
b'\x00\x00' \
|
||
|
b'\xf0\x00' \
|
||
|
b'\x35' \
|
||
|
b'\x00'
|
||
|
)
|
||
|
|
||
|
assert(p[Ether].dst == '00:02:04:06:08:0a')
|
||
|
assert(p[Ether].src == '01:03:05:07:09:0b')
|
||
|
assert(p[Ether].type == 0x8892)
|
||
|
assert(p[ProfinetIO].frameID == 0x8010)
|
||
|
assert(isinstance(p[ProfinetIO].payload, PNIORealTimeCyclicPDU))
|
||
|
assert(len(p[PNIORealTimeCyclicPDU].data) == 3)
|
||
|
assert(isinstance(p[PNIORealTimeCyclicPDU].data[0], scapy.config.conf.raw_layer))
|
||
|
assert(p[PNIORealTimeCyclicPDU].data[0].data == b'\x01\x02\x03\x04\x05')
|
||
|
assert(isinstance(p[PNIORealTimeCyclicPDU].data[1], scapy.config.conf.raw_layer))
|
||
|
assert(p[PNIORealTimeCyclicPDU].data[1].data == b'\x01\x02\x03')
|
||
|
assert(isinstance(p[PNIORealTimeCyclicPDU].data[2], scapy.config.conf.raw_layer))
|
||
|
assert(p[PNIORealTimeCyclicPDU].data[2].data == b'\x01\x02')
|
||
|
assert(p[PNIORealTimeCyclicPDU].padding == b'\x00' * 2)
|
||
|
assert(p[PNIORealTimeCyclicPDU].cycleCounter == 0xf000)
|
||
|
assert(p[PNIORealTimeCyclicPDU].dataStatus == 0x35)
|
||
|
assert(p[PNIORealTimeCyclicPDU].transferStatus == 0)
|
||
|
|
||
|
p = Ether(b'\x00\x02\x04\x06\x08\x0a\x01\x03\x05\x07\x09\x0B\x88\x92\x80\x00\x01\x02\x03\x04\xf0\x00\x35\x00')
|
||
|
assert(p[Ether].dst == '00:02:04:06:08:0a')
|
||
|
assert(p[Ether].src == '01:03:05:07:09:0b')
|
||
|
assert(p[Ether].type == 0x8892)
|
||
|
assert(p[ProfinetIO].frameID == 0x8000)
|
||
|
assert(isinstance(p[ProfinetIO].payload, PNIORealTimeCyclicPDU))
|
||
|
assert(len(p[PNIORealTimeCyclicPDU].data) == 1)
|
||
|
assert(isinstance(p[PNIORealTimeCyclicPDU].data[0], PNIORealTimeCyclicDefaultRawData))
|
||
|
assert(p[PNIORealTimeCyclicDefaultRawData].data == b'\x01\x02\x03\x04')
|
||
|
assert(p[PNIORealTimeCyclicPDU].padding == b'')
|
||
|
assert(p[PNIORealTimeCyclicPDU].cycleCounter == 0xf000)
|
||
|
assert(p[PNIORealTimeCyclicPDU].dataStatus == 0x35)
|
||
|
assert(p[PNIORealTimeCyclicPDU].transferStatus == 0)
|
||
|
True
|
||
|
|
||
|
= PROFIsafe parsing (query with F_CRC_SEED=0)
|
||
|
p = PROFIsafe.build_PROFIsafe_class(PROFIsafeControl, 2)(b'\x80\x80\x40\x01\x02\x03')
|
||
|
assert(p.data == b'\x80\x80')
|
||
|
assert(p.control == 0x40)
|
||
|
assert(p.crc == 0x010203)
|
||
|
True
|
||
|
|
||
|
= PROFIsafe parsing (query with F_CRC_SEED=1)
|
||
|
p = PROFIsafe.build_PROFIsafe_class(PROFIsafeControlCRCSeed, 2)(b'\x80\x80\x40\x01\x02\x03\x04')
|
||
|
assert(p.data == b'\x80\x80')
|
||
|
assert(p.control == 0x40)
|
||
|
assert(p.crc == 0x01020304)
|
||
|
True
|
||
|
|
||
|
= PROFIsafe parsing (response with F_CRC_SEED=0)
|
||
|
p = PROFIsafe.build_PROFIsafe_class(PROFIsafeStatus, 1)(b'\x80\x40\x01\x02\x03')
|
||
|
assert(p.data == b'\x80')
|
||
|
assert(p.status == 0x40)
|
||
|
assert(p.crc == 0x010203)
|
||
|
True
|
||
|
|
||
|
= PROFIsafe parsing (response with F_CRC_SEED=1)
|
||
|
p = PROFIsafe.build_PROFIsafe_class(PROFIsafeStatusCRCSeed, 1)(b'\x80\x40\x01\x02\x03\x04')
|
||
|
assert(p.data == b'\x80')
|
||
|
assert(p.status == 0x40)
|
||
|
assert(p.crc == 0x01020304)
|
||
|
True
|
||
|
|
||
|
= PROFIsafe building (query with F_CRC_SEED=0)
|
||
|
p = PROFIsafe.build_PROFIsafe_class(PROFIsafeControl, 2)(data = b'\x81\x80', control=0x40, crc=0x040506)
|
||
|
assert(raw(p) == b'\x81\x80\x40\x04\x05\x06')
|
||
|
|
||
|
= PROFIsafe building (query with F_CRC_SEED=1)
|
||
|
p = PROFIsafe.build_PROFIsafe_class(PROFIsafeControlCRCSeed, 2)(data = b'\x81\x80', control=0x02, crc=0x04050607)
|
||
|
assert(raw(p) == b'\x81\x80\x02\x04\x05\x06\x07')
|
||
|
|
||
|
= PROFIsafe building (response with F_CRC_SEED=0)
|
||
|
p = PROFIsafe.build_PROFIsafe_class(PROFIsafeStatus, 3)(data = b'\x01\x81\x00', status=0x01, crc=0x040506)
|
||
|
assert(raw(p) == b'\x01\x81\x00\x01\x04\x05\x06')
|
||
|
|
||
|
= PROFIsafe building (response with F_CRC_SEED=1)
|
||
|
p = PROFIsafe.build_PROFIsafe_class(PROFIsafeStatusCRCSeed, 3)(data = b'\x01\x81\x80', status=0x01, crc=0x04050607)
|
||
|
assert(raw(p) == b'\x01\x81\x80\x01\x04\x05\x06\x07')
|
||
|
|
||
|
conf.debug_dissector = old_conf_dissector
|