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



Reply via email to