man, these samples are pretty long to go with, but from overall issue it
seems like we need to do reflect multi layer protocols in a better way.
As far I understand the problem - it boils down to underlying buffer
which is started with BE and we need to read things using LE.
I see issue in two areas:
1) We have no BE/LE switch for buffer other than constructor argument
2) The switch in case of profinet is contextual and depends on protocol
layer we're currently in.
Given the 1 - which is currently the base layer I think going with any
of ways you mentioned we will probably need a "flip" operation to swap
endianness or so.
Personally I think making the switch at the type level is easiest to get
since we had this attempt in the past. Having an extra block for it
seems like a bigger change (yet we discuss another block idea).
Making a contextual switches for fields is doable since new APIs made by
Sebastian for reader/writer allow to pass "withOptions" which would
really work well in this scenario.
So if you'd ask me I'd opt for type level and field level switch. With
this we could in theory simulater a block with a a complex type having a
endianness switch.
Best,
Łukasz
On 26.09.2021 15:59, Christofer Dutz wrote:
Hi all,
I'm currently working on the PROFINET ... here I have the challenge, that I
have multiple layers of different protocols.
UDP -> DCE/RPC -> PROFINET IO CM (Block Container) -> PROFINET IO CM Blocks
The problem is:
* UDP is Big Endian
* PROFINET IO CM Blocks are Big Endian
* DCE/RPC is big or little endian depending on a variable inside the
DCE/RPC packet
* Profinet IO CM (block container) is the same encoding as the DCE/RPC
packet
At first I thought if I force my request to big endian, then the response would
be accordingly.
Unfortunately that's not the case ... So if I send a request with Big Endian
DCE/RPC (Hence, makeing the entire message Big endian). I still get a response
back with DCE/RPC in Little Endian. So I have to be able to handle both.
Now I had multiple ideas:
* A new sort of block "endianessSwitch" (or a better name) ... which takes an
argument and which simply switches the endianess of the buffers to the given one. This would
probably be pretty simple to implement with a "try-finally" block in Java.
* We add an optional additional option to the complex-type declarations.
This would however have us split types into two.
Here comes the example of the PN DCE/RPC packet with an endianessSwitch:
[discriminatedType 'DceRpc_Packet'
[const uint 8 'version' '0x04'
]
[discriminator DceRpc_PacketType 'packetType'
]
[typeSwitch 'packetType'
['REQUEST' DceRpc_Packet_Req
[reserved uint 1 '0x0'
]
[const uint 1 'broadcast' '0'
]
[const uint 1 'idempotent' '1'
]
[const uint 1 'maybe' '0'
]
[const uint 1 'noFragmentAcknowledgeRequested' '0'
]
[const uint 1 'fragment' '0'
]
[const uint 1 'lastFragment' '0'
]
[reserved uint 1 '0x0'
]
]
['RESPONSE' DceRpc_Packet_Res
[reserved uint 1 '0x0'
]
[const uint 1 'broadcast' '0'
]
[const uint 1 'idempotent' '0'
]
[const uint 1 'maybe' '0'
]
[const uint 1 'noFragmentAcknowledgeRequested' '1'
]
[const uint 1 'fragment' '0'
]
[const uint 1 'lastFragment' '1'
]
[reserved uint 1 '0x0'
]
]
['REJECT' DceRpc_Packet_Rej
[reserved uint 1 '0x0'
]
[const uint 1 'broadcast' '0'
]
[const uint 1 'idempotent' '0'
]
[const uint 1 'maybe' '0'
]
[const uint 1 'noFragmentAcknowledgeRequested' '0'
]
[const uint 1 'fragment' '0'
]
[const uint 1 'lastFragment' '0'
]
[reserved uint 1 '0x0'
]
]
]
[reserved uint 6 '0x00'
]
[const uint 1 'cancelWasPending' '0'
]
[reserved uint 1 '0x0'
]
[simple IntegerEncoding 'integerEncoding'
]
[simple CharacterEncoding 'characterEncoding'
]
[simple FloatingPointEncoding 'floatingPointEncoding'
]
[endianessSwitch 'integerEncoding == IntegerEncoding.BIG_ENDIAN'
[const uint 8 'serialHigh' '0x00'
]
[const uint 8 'serialLow' '0x00'
]
// 4.10.3.2.8 Coding of the field RPCObjectUUID
DEA00000-6C97-11D1-8271-{instanceOrNodeNumber}{deviceId}{vendorId}
// Apache Vendor Id: 0x060B
// PLC4X Profinet Driver Device ID (can be chosen freely): 0xCAFE
// NOTE: We can get the Device-Id and Vendor-Id from the PN-DCP search
result of the browser.
[const uint 32 'uuid1' '0xDEA00000'
]
[const uint 16 'uuid2' '0x6C97'
]
[const uint 16 'uuid3' '0x11D1'
]
[const uint 16 'uuid4' '0x8271'
]
[simple uint 16 'instanceOrNodeNumber'
]
[simple uint 16 'deviceId'
]
[simple uint 16 'vendorId'
]
// 4.10.3.2.9
// Device Interface: DEA00001-6C97-11D1-8271-00A02442DF7D
// Controller Interface: DEA00002-6C97-11D1-8271-00A02442DF7D
// Supervisor Interface: DEA00003-6C97-11D1-8271-00A02442DF7D
// Parameter Server Interface: DEA00004-6C97-11D1-8271-00A02442DF7D
[const uint 32 'interface1' '0xDEA00001'
]
[const uint 16 'interface2' '0x6C97'
]
[const uint 16 'interface3' '0x11D1'
]
[const uint 16 'interface4' '0x8271'
]
[const uint 16 'interface5' '0x00A0'
]
[const uint 32 'interface6' '0x2442DF7D'
]
// 4.10.3.2.10
// The Controller and the Device generate the uuid for each AR
(Application Relationship) and use them as long as the AR exists
[const uint 32 'activity'
]
[const uint 16 'activity2' '0x0000'
]
[const uint 16 'activity3' '0x1010'
]
[const uint 16 'activity4' '0xAA25'
]
[const uint 32 'activity5' '0x606D3C3D'
]
[const uint 16 'activity6' '0xA9A3'
]
[const uint 32 'serverBootTime'
]
[const uint 32 'interfaceVer' '0x00000001'
]
[const uint 32 'sequenceNumber'
]
[const DceRpc_Operation 'operation'
]
[const uint 16 'interfaceHint' '0xFFFF'
]
[const uint 16 'activityHint' '0xFFFF'
]
[const uint 16 'fragmentLength'
'payload.lengthInBytes']
[const uint 16 'fragmentNum' '0x0000'
]
[const uint 8 'authProto' '0x00'
]
[const uint 8 'serialLow2' '0x00'
]// TODO: Check this ...
[simple PnIoCm_Packet 'payload' ['packetType']
]
]
]
The endianess switching would also apply tot he PnIoCm_Packet automatically.
The other option would be:
[discriminatedType 'DceRpc_Packet'
[const uint 8 'version' '0x04'
]
[discriminator DceRpc_PacketType 'packetType'
]
[typeSwitch 'packetType'
['REQUEST' DceRpc_Packet_Req
[reserved uint 1 '0x0'
]
[const uint 1 'broadcast' '0'
]
[const uint 1 'idempotent' '1'
]
[const uint 1 'maybe' '0'
]
[const uint 1 'noFragmentAcknowledgeRequested' '0'
]
[const uint 1 'fragment' '0'
]
[const uint 1 'lastFragment' '0'
]
[reserved uint 1 '0x0'
]
]
['RESPONSE' DceRpc_Packet_Res
[reserved uint 1 '0x0'
]
[const uint 1 'broadcast' '0'
]
[const uint 1 'idempotent' '0'
]
[const uint 1 'maybe' '0'
]
[const uint 1 'noFragmentAcknowledgeRequested' '1'
]
[const uint 1 'fragment' '0'
]
[const uint 1 'lastFragment' '1'
]
[reserved uint 1 '0x0'
]
]
['REJECT' DceRpc_Packet_Rej
[reserved uint 1 '0x0'
]
[const uint 1 'broadcast' '0'
]
[const uint 1 'idempotent' '0'
]
[const uint 1 'maybe' '0'
]
[const uint 1 'noFragmentAcknowledgeRequested' '0'
]
[const uint 1 'fragment' '0'
]
[const uint 1 'lastFragment' '0'
]
[reserved uint 1 '0x0'
]
]
]
[reserved uint 6 '0x00'
]
[const uint 1 'cancelWasPending' '0'
]
[reserved uint 1 '0x0'
]
[simple IntegerEncoding 'integerEncoding'
]
[simple CharacterEncoding 'characterEncoding'
]
[simple FloatingPointEncoding 'floatingPointEncoding'
]
[simple DceRpc_PacketPayload 'payload' endianess='integerEncoding'
]
]
[type 'DceRpc_PacketPayload'
[const uint 8 'serialHigh' '0x00'
]
[const uint 8 'serialLow' '0x00'
]
// 4.10.3.2.8 Coding of the field RPCObjectUUID
DEA00000-6C97-11D1-8271-{instanceOrNodeNumber}{deviceId}{vendorId}
// Apache Vendor Id: 0x060B
// PLC4X Profinet Driver Device ID (can be chosen freely): 0xCAFE
// NOTE: We can get the Device-Id and Vendor-Id from the PN-DCP search
result of the browser.
[const uint 32 'uuid1' '0xDEA00000'
]
[const uint 16 'uuid2' '0x6C97'
]
[const uint 16 'uuid3' '0x11D1'
]
[const uint 16 'uuid4' '0x8271'
]
[simple uint 16 'instanceOrNodeNumber'
]
[simple uint 16 'deviceId'
]
[simple uint 16 'vendorId'
]
// 4.10.3.2.9
// Device Interface: DEA00001-6C97-11D1-8271-00A02442DF7D
// Controller Interface: DEA00002-6C97-11D1-8271-00A02442DF7D
// Supervisor Interface: DEA00003-6C97-11D1-8271-00A02442DF7D
// Parameter Server Interface: DEA00004-6C97-11D1-8271-00A02442DF7D
[const uint 32 'interface1' '0xDEA00001'
]
[const uint 16 'interface2' '0x6C97'
]
[const uint 16 'interface3' '0x11D1'
]
[const uint 16 'interface4' '0x8271'
]
[const uint 16 'interface5' '0x00A0'
]
[const uint 32 'interface6' '0x2442DF7D'
]
// 4.10.3.2.10
// The Controller and the Device generate the uuid for each AR
(Application Relationship) and use them as long as the AR exists
[const uint 32 'activity'
]
[const uint 16 'activity2' '0x0000'
]
[const uint 16 'activity3' '0x1010'
]
[const uint 16 'activity4' '0xAA25'
]
[const uint 32 'activity5' '0x606D3C3D'
]
[const uint 16 'activity6' '0xA9A3'
]
[const uint 32 'serverBootTime'
]
[const uint 32 'interfaceVer' '0x00000001'
]
[const uint 32 'sequenceNumber'
]
[const DceRpc_Operation 'operation'
]
[const uint 16 'interfaceHint' '0xFFFF'
]
[const uint 16 'fragmentNum' '0x0000'
]
[const uint 16 'activityHint' '0xFFFF'
]
[const uint 16 'fragmentLength'
'payload.lengthInBytes']
[const uint 8 'authProto' '0x00'
]
[const uint 8 'serialLow2' '0x00'
]// TODO: Check this ...
[simple PnIoCm_Packet 'payload' ['packetType']
]
]
Thinking about it ... we might even have a third option: Explicitly defininig
the endianess in the complex type (Just a quick thought perhaps name things
differently):
// Big Endian
[discriminatedType 'PnIoCm_Block' endianess=BIG_ENDIAN
[discriminator PnIoCm_BlockType 'blockType' ]
[implicit uint 16 'blockLength' 'lengthInBytes - 4']
[simple uint 8 'blockVersionHigh' ]
[simple uint 8 'blockVersionLow' ]
...
I have to admit, that I sort of like option 1 and 3 ... but we might even
support all 3 variants. The implementation shouldn't be too hard.
And thinking even more about it ... perhaps this concept of adding name=value pairs to
the type declaration and the fields could also be used to controll Sebasitan's
"try" flag.
Chris