liftoff-sr / CIPster

Ethernet/IP (Common Industrial Protocol) stack in C++. This started as a C to C++ conversion of C based OpENer.
Other
131 stars 49 forks source link

Stack re-entrance issues. #25

Closed acidtonic closed 1 year ago

acidtonic commented 2 years ago

I am having issues with some kind of global state or re-entrance issue if I completely bring the stack down and simply bring it back up again.

The behavior is that the stack comes up and properly binds/listens. It accepts the connection and starts communication, but oddly none of the new ByteBufs ever see updated values from the PLC.

I am not stopping via a signal handler but rather the g_endStack variable, cleaning up by calling NetworkHandlerFinish() and finally ShutdownCipStack();

I then bring it back up again later by repeating the initialization procedure, I have tried using a different connection number or reusing it as a test and both ways result in the same behavior described above. I have also tried registering assemblies at a different CIP address upon restarting it and still the new address assemblies do not show updated values as the PLC communicates.

Just curious if I am missing anything or if this is something the library was coded to do? I simply desire to stop the CIP server, register different assemblies at different addresses, and bring it back up again.

liftoff-sr commented 2 years ago

Zach, It's likely that this was never tested. In my own use I terminate the process after shutting down the stack, so I never ran into this. Are you sure you cannot do the same? Have you tried registering assemblies late in the game?

The behaviour you seek seems doable. it is probably in a commented out call to a destructor, or several. Often I do not call destructors if it's in a code path toward process termination.

Somebody would have to run the stack under a debugger, and compare what happens in stack shutdown as compared to stack start up. It would mean taking notes of all the actions during startup, and then verifying that stack shutdown not only does those steps in reverse, but restores all static variables to their original state.

I am not volunteering to do this at this time, but would obviously answer questions and welcome a patch.

acidtonic commented 2 years ago

Thanks! That is exactly the thing I was feeling out. It must be close as everything indeed comes up I just think somewhere a global or pointer is not updated so the writes are hitting the old assembly addresses.

I will likely spend some time figuring this out as I am mapping the engine into a scripting language such that the lifecycle is out of my hands and users can start/stop scripts to run various plc programs without exiting the application so I will definitely need to address this.

Thanks again. Cheers.

acidtonic commented 2 years ago

Still fighting this, I'm about to just start over with a clean slate.

Unfortunately my fix is basically going down the rabbit hole of rewriting everything to not be global static. Callbacks switched to functors over globals, etc. fd_set is not happy being static after re-entrance and as I keep moving things out I'm starting to see lots of unchecked error paths without tracing or logging. Half the paths blindly return OK when lots of serious things are wrong like bad sequence numbers.

Upon bringing things back up without the process exiting, select() never returns anything other than 0 for the read set and thus the udp handler never gets drained. I mucked around with making fd_set non static and it seems oddly now I hit blocking behavior in places where it's completely unexpected.

I can validate with wireshark that packets continue to flow, new cip sessions are registered, sockets rebinded with new fds, but then select() stops working. I can see writes going out but no reads.

liftoff-sr commented 2 years ago

Questions: 1) Which operating systems are you targeting with your scripting tool? 2) What language is the scripting tool? 3) What tool are your using to create the bindings to that scripting language? 4) Did you make the list I suggested of activities during stack initialization? (If so please send it to me.)