dnp3 Parser and Simulator
- 14 Devlogs
- 50 Total hours
Reads, Produces, Analyzes, and Simulates dnp3 packets passing between mutliple master and client instances. dnp3 is a protocol used often in EE with SCADA systems
Reads, Produces, Analyzes, and Simulates dnp3 packets passing between mutliple master and client instances. dnp3 is a protocol used often in EE with SCADA systems
Just finished adding the ability to create a packet based on parameters rather than requiring a premade packet.
Basicly, instead of needing something like ,
uint8_t input[] = {
0x05, 0x64, ...
};
dnp3p_st packet_s = mkPacket(input);
You can now just use a function and pass in parameters. Then, since this is just a fancy memory structure, a simple memcpy into an appropriately sized array will give you both! (The raw packet, and the structure)
uint8_t packet[30] = {0};
// did some renaming, uses dnp3Lib for library funcs now
dnp3p_st packet_s = dnp3Lib_mkPacket_manual(1, 0, 30, 2, 0, 10);
// params are: destination, source, group, variation, startIndex, and stopIndex
memcpy(&packet, &packet_s, 30);
This will give you basicly the same thing as the first example, meaning you can create a packet based on a discription, rather than a discription based on a packet.
This will be very useful when creating responses from to the client from the server, or vice versa.
Should be noted that this is technically not very complete, but it is just an example for now.
On the topic of creating a packet based on a discription, I also created some functions to create packets for common messages, including;
These will be useful when making connections, and sending responses to requests and replies.
Still got a ways to go in terms of completion, but I’m hoping to ramp up my speed, been a little busy the past couple weeks.
Added the ability to pick a custom socket or server ip. Also actualy pushed becuase I forgot last time
Just finished getting the client server connection working.
Next thing to do is to interpret the packet on the server side, then create a reply based on the request.
I just pushed some changes and merged main and dev. This update I basically just made some fixes that should make the parser more accurate overall, but there are still a few lingering bugs.
The main change I made was adding a system to clean the packet of any CRC bytes from within. Normally, a packet contains 2 bytes of CRC spread around it, with 2 after the end of the header, and after that 2 after every 16 bytes.
This change will allow my data handler to not accidentally ingest CRC bytes, which would mess up the spacing of each data segment, and result in randomness.
I also added a function to calculate the size of a packet based on the length information stored within it, rather than the physical memory size of it. This is better, as I can call it from any function and not have to pass in the size as a param.
Overall, I am around 90% done with the backend, and just have a couple bugs to fix.
I have attached a screen shot of some of the new features.
This week I finished parsing DNP3 Group 1 Variation 1, the first object type supported by the parser. That means the parser can now decode binary input status values from real DNP3 traffic instead of just stopping at the object header.
I also got some practice using GDB to debug my programs! Super useful as I ran into some segmentation faults that would have been impossible to fix without it!
DNP3 (Distributed Network Protocol 3) is an industrial control protocol used by electric utilities, water systems, and other critical infrastructure. Communication happens between a Master (SCADA server) and one or more Outstations (RTUs, relays, PLCs, etc.).
A packet is made up of several layers:
The packet always begins with the start bytes:
0x05 0x64
These identify the frame as DNP3.
The rest of the link header contains:
This layer is responsible for moving frames between devices and detecting transmission errors.
The transport header is only one byte long, but it’s important because it:
The application header tells the receiver what operation is being requested.
Examples include:
It also contains application control flags and an application sequence number.
After the application header come one or more object headers. These tell the parser what type of data follows.
An object header contains:
For example:
Group 1 Variation 1
represents packed binary input values.
The qualifier then specifies how many objects are present and how they are indexed.
Finally comes the actual payload.
For Group 1 Variation 1, each bit represents the current state of a binary input:
1 = ON0 = OFFThe parser reads these bytes and expands them into individual points that can be displayed or analyzed.
Next up is adding support for additional object groups and variations so the parser can decode more real-world DNP3 traffic.
Its once again that time. This week I worked on the object header, updated the printing on the DLC parsing, as well as fixing a couple big bugs.
The transport header consists of 5 parts, and its main job is to explains the format, and ordering of the data in the packet. Note that there are multiple object and data blocks in a single packet, but currently I can only parse 1 at most.
Ran into a couple bugs, but I’ll just mention 2.
When filling the range (part of the object header) I ran into some errors.
Incorrect DLC data being printed.
Thanks for enduring that yap fest. Next update will probably be when I add in data parsing, unless I make some major change.Hope you enjoyed it.
Check out the GitHub:
https://github.com/arnabmondal-eg/dnp3
And have a safe, fun, and patriotic Fourth of July!
I genuinely dont know how its been 7 hours. I feel like the majority of my time is fixing bugs and trying to get my code to compile.
It’s been a while since my last devlog, and I have spent the past 7 hours adding a good bit. Main thing is that almost the entire packet is now being parsed, or atleast the hard part.
The entire header, dlc byte, transport header, and application header are parsed, so the only thing left is the Object headers and the data itself.
I also created a new struct to hold all of these functions, so you can just pass in a hex and get all this data out, no need to call each portions functionality anymore.
Moving forward, I have a couple goals/milestones I have to hit before I can ship this project:
Just did a little rework to move all header files to the inc/ folder. This should make it less cluttered, but we will see how this holds up.
Next things on the chopping block are:
Check out the GitHub! https://github.com/arnabmondal-eg/dnp3
Its been a while…
Hello! I have been working on the parser for a while. This post will be an explanation of where I am in the project so far.
I wouldn’t go as far as to say that I am close to done yet, but I’m definitely nearing the halfway point.
The past 2 days I have reworked how packets are interpreted, as well as adding some new helper utilities/functions to make future decoding easier. I also implemented a full bit-by-bit decomposition of the DLC byte in a header!
This was super fun and rewarding as I learned a lot about bit-wise operations in C, as well as strengthening my understand of memory and how it is transferred through a program.
I also learned I can’t spell! Wrote valid as “vaild” over 10 times! I also learned that C doesn’t pass the size of an array automatically! Super fun bugs! \s
Feel free to critique my code on the GitHub! https://github.com/arnabmondal-eg/dnp3
Finished reworking the Parser UI. It uses a split pane so you can get as much or as little space as you need. Features a text input area on the left with a parse button on the bottom, and a tree view on the right (to see the parsed packet contents) with a valid indicator on the bottom.
Nothing is hooked up yet to logic, so that is coming up soon on the list!
Swing is definitely kind of tedious to write though!
Check out the github: https://github.com/arnabmondal-eg/dnp3
Just finished writing the final portion of the header validity checker, which uses a brand new CRC byte calculator!
Very Fun to make, as I really got to dig deep into bit-wise operations for the first time, but definitely challenging!
In terms of the backend, there is still much to do, but I am getting close to completion. The frontend still is in rough shape however.
Making Good Progress!
Improved project structure, worked on README, added tasks to make compiling easier.
Really want this to be usable, so I am putting in the work to make it simple to understand and use.
Also I hated writing this project tree. Leme know if there is some tool i can use to do this automatically in the future
Working on the parser backend.
Currently planning for all the backend to be in C, while I handle the frontend in Java using Swing.
While this may seem like an ancient technology, most production SCADA applications use Swing for its long term support. And Java and C are obvious choices aswell.
Heres a pic of the header part of the interpreter, and some work in progress ui. Much more work to come!