Convert protocol to Rust: ENIP
ENIP implementation already exists for Suricata but in order to make it more secure and trustworthy, we'd like to convert it to Rust.
Below is a (very) brief step by step overview of going about the implementation:
- Get to know more about the protocol from the official RFC.
- Match the RFC defined header structs and handling with C code and understand and note down the flow and handling.
- Create basic header structures in Rust. (These do not have to be same as C but should follow the protocol standards (e.g. alignment and data types of certain fields) as per the RFC)
- Use test data from C for headers and try to write a parser such that those tests pass as they did in C.
- After the header parsers work in Rust, move to UDP implementation of packet handling (start with creating a <Protocol>State struct to store all important info).
- Try to replicate UDP handling in C for the protocol and make unit tests pass for one transaction only.
- Integrate with C, make sure the tests pass.
- Find PCAPs for the application layer protocol over UDP covering all the modes (in case there are diff modes of operation for a protocol e.g. diff versions of protocol)
- Write a (few) suricata-verify tests (you could use createst.py to create those for you with the PCAP) with those PCAPS and make sure all the tests pass.
This completes UDP conversion of the protocol and some basic chassis for protocol over TCP (structures and header parsing).
10. Try and replicate the TCP implementation in C of the protocol.
11. Learn more about stream handling and how Suricata does internal reassembly of packets, etc.
12. Repeat steps 6-9 for TCP.
This completes TCP conversion of the protocol as well but all works only for one transaction.
13. Try adding more transactions to tests and make them pass.
14. Make sure suricata-verify tests pass too.
This completes multi-transaction handling of the protocols.
15. Check out TCP stream gap handling for other protocols and try to implement the same for the protocol in Rust.
16. Enable gap handling for the protocol in C and integrate Rust code.
17. Find out or create PCAPs with TCP stream gaps, add tests with them to suricata-verify, make sure they pass.
This almost completes the conversion of the protocol.
18. Enable logging for the protocol, convert json logging from C to Rust.
19. Test that logging results pass with `--enable-debug-validation` and jsonbuilder does not panic anywhere.
20. Write suricata-verify tests for the same, make sure they pass.
This completes the protocol conversion to Rust. Now, fix bugs as when they arrive. :)
- Take inspiration from other protocols, smaller/popular protocols (e.g. DNS, SIP) are easier to follow.
- Git is your best friend. (e.g. to see how a particular protocol's conversion progressed check all the closed PRs corresponding to that, a lot of useful content might be there)
Updated by Victor Julien about 2 years ago
PCAPS to test with: https://github.com/scy-phy/bro-cip-enip/tree/master/testing/btest/Traces/enip