PROFNET IO RTC

PROFINET IO是由实时循环(RTC)层等不同层组成的工业协议,用于数据交换。然而,这个rtc层是有状态的,并且依赖于通过另一层发送的配置:profinet的dce/rpc端点。此配置定义每个交换数据块必须位于RTC中的位置。 data 以及这个相同缓冲区的长度。构建这样的包比其他协议要复杂一些。

RTC数据包

构建RTC时要做的第一件事 data 缓冲区用于实例化表示一段数据的每个Scapy数据包。一些基本数据包包括:

  • ProfinetIO :该大楼为PROFINET包挡路。可以位于Ether()或UDP()之上

  • PROFIsafe :执行功能安全的PROFIsafe配置文件

  • PNIORealTime_IOxS :IO使用者或提供者状态字节

按如下方式实例化数据包:

>>> load_contrib('pnio')
>>> raw(ProfinetIO()/b'AAA')
b'\x00\x00AAA'
>>> raw(PROFIsafe.build_PROFIsafe_class(PROFIsafeControl, 4)(data = b'AAA', control=0x20, crc=0x424242))
b'AAA\x00 BBB'
>>> hexdump(PNIORealTime_IOxS())
0000   80                                                 .

RTC包

现在可以实例化数据分组,可以构建整个RTC分组。 PNIORealTimeCyclicPDU 包含一个字段 data 这是要添加到缓冲区中的所有数据包的列表,但是,如果没有配置,Scapy将无法剖析它::

>>> load_contrib('pnio')
>>> p=PNIORealTimeCyclicPDU(cycleCounter=1024, data=[
... PNIORealTime_IOxS(),
... PNIORealTimeCyclicPDU.build_fixed_len_raw_type(4)(data = b'AAA') / PNIORealTime_IOxS(),
... PROFIsafe.build_PROFIsafe_class(PROFIsafeControl, 4)(data = b'AAA', control=0x20, crc=0x424242)/PNIORealTime_IOxS(),
... ])
>>> p.show()
###[ PROFINET Real-Time ]###
  \data      \
   |###[ PNIO RTC IOxS ]###
   |  dataState = good
   |  instance  = subslot
   |  reserved  = 0x0
   |  extension = 0
   |###[ FixedLenRawPacketLen4 ]###
   |  data      = 'AAA'
   |###[ PNIO RTC IOxS ]###
   |     dataState = good
   |     instance  = subslot
   |     reserved  = 0x0
   |     extension = 0
   |###[ PROFISafe Control Message with F_CRC_Seed=0 ]###
   |  dat(      = 'AAA'
   |  control   = Toggle_h
   |  crc       = 0x424242
   |###[ PNIO RTC IOxS ]###
   |     dataState = good
   |     instance  = subslot
   |     reserved  = 0x0
   |     extension = 0
  padding   = ''
  cycleCounter= 1024
  dataStatus= primary+validData+run+no_problem
  transferStatus= 0

为了使SCapy能够正确地解析它,还必须配置层,使其知道每个数据在缓冲区中的位置。此配置保存在字典中 conf.contribs["PNIO_RTC"] 可以使用 conf.contribs["PNIO_RTC"].update 方法。字典中的每一项都使用元组 (Ether.src, Ether.dst, ProfinetIO.frameID) 作为关键,能够分离每个通信的配置。然后,每个值是描述数据分组的类的列表。如果我们继续前面的示例,下面是要设置的配置:

>>> e=Ether(src='00:01:02:03:04:05', dst='06:07:08:09:0a:0b') / ProfinetIO(frameID="RT_CLASS_1") / p
>>> e.show2()
###[ Ethernet ]###
  dst       = 06:07:08:09:0a:0b
  src       = 00:01:02:03:04:05
  type      = 0x8892
###[ ProfinetIO ]###
     frameID   = RT_CLASS_1 (8000)
###[ PROFINET Real-Time ]###
        \data      \
         |###[ PROFINET IO Real Time Cyclic Default Raw Data ]###
         |  data      = '\\x80AAA\x00\\x80AAA\x00 BBB\\x80'
        padding   = ''
        cycleCounter= 1024
        dataStatus= primary+validData+run+no_problem
        transferStatus= 0
>>> conf.contribs["PNIO_RTC"].update({('00:01:02:03:04:05', '06:07:08:09:0a:0b', 0x8000): [
... PNIORealTime_IOxS,
... PNIORealTimeCyclicPDU.build_fixed_len_raw_type(4),
... PNIORealTime_IOxS,
... PROFIsafe.build_PROFIsafe_class(PROFIsafeControl, 4),
... PNIORealTime_IOxS,
... ]})
>>> e.show2()
###[ Ethernet ]###
  dst       = 06:07:08:09:0a:0b
  src       = 00:01:02:03:04:05
  type      = 0x8892
###[ ProfinetIO ]###
     frameID   = RT_CLASS_1 (8000)
###[ PROFINET Real-Time ]###
        \data      \
         |###[ PNIO RTC IOxS ]###
         |  dataState = good
         |  instance  = subslot
         |  reserved  = 0x0
         |  extension = 0
         |###[ FixedLenRawPacketLen4 ]###
         |  data      = 'AAA'
         |###[ PNIO RTC IOxS ]###
         |  dataState = good
         |  instance  = subslot
         |  reserved  = 0x0
         |  extension = 0
         |###[ PROFISafe Control Message with F_CRC_Seed=0 ]###
         |  data      = 'AAA'
         |  control   = Toggle_h
         |  crc       = 0x424242
         |###[ PNIO RTC IOxS ]###
         |  dataState = good
         |  instance  = subslot
         |  reserved  = 0x0
         |  extension = 0
        padding   = ''
        cycleCounter= 1024
        dataStatus= primary+validData+run+no_problem
        transferStatus= 0

如果没有为给定偏移量配置数据包,则默认为 PNIORealTimeCyclicDefaultRawData