RS485 BACnet sniffing

How do you get to the Transmit pane if it is greyed out(un-selectable) ?

The Transmit pane is greyed out in the Serial Tap plugin because it's a read-only sniffer device. You can transmit using the Serial Terminal plugin and a regular USB-to-RS485 adapter.

I tried using RegEx Markup with default Session settings and it does not want to break the packets up.

After adding "Force Latin-1 encoding" to the Log Regex Markup settings I'm getting color highlights but no new timestamps when it see's the pattern.

Right, I should have mentioned that Latin-1 encoding might be necessary when the pattern contains invalid UTF-8 sequences (such as \x55\xff).

In general, using timestamps to split the data stream into packets is not a reliable enough approach (unless every captured byte is precisely timestamped). Serial Tap doesn't do that; instead, timestamps are assigned to the whole blocks of data as they are received over USB. There are multiple layers of buffering involved (at both the Serial Tap and the PC side), so the timing could be distorted to some extent, and bytes from different packets could end up being read within the same block. It's much better to parse the stream and split it into packets based on the actual data contents.

I've created a draft of the BACnet MSTP protocol analyzer for you: https://tibbo.com/downloads/archive/ioninja/.internal/scripts/BacNetMsTp.7z

It breaks the stream into BACnet frames, gives a human-readable representation of the frame header, and highlights the payload (if any).

ae83d05d-099d-4d65-a9e8-3902fff59533-image.png

Feel free to modify or extend it to your liking. However, please note, that ioninja-5.5.0 has a regression that prevents log.representStruct() used in this script from operating properly (it's already fixed but the service release is not out yet). So, to use this plugin, please either (a) roll back to ioninja-5.4.2 or (b) use the internal build ioninja-5.5.0-a: https://tibbo.com/downloads/archive/ioninja/.internal/prerelease/ioninja-5.5.0-a-windows-amd64.7z

Let me know if it works for you, and feel free to ask me anything about the internal implementation of the analyzer script.

P.S. FYI, just as with any representation generated by log.representStruct(), you can click on a header field, and the corresponding data bytes will be highlighted below -- so that you can see the mapping between header fields and data bytes.

Wow, this is great. Couple questions:

  • The Pre-release does not have an install script(msi file.) Can I just drop the folders into the current C:\Program Files\Tibbo\IO Ninja directory?

Loaded your analyzer(on version 5.5.0) using Settings->Add-on Plugins->Layers->Add and selected BacNetMsTp.njplg(this was a guess).

  • It ran very well except that after a large packet came through it seemed to stop(see timestamp 16:21:35 +00:06:437. Looked through the scripts(everything you sent) to see if there was a max number of bytes for a packet but did not find one.

Capture.JPG

Thanks again.

Version 5.5.0 will not display the contents of BACnet headers properly (after you click [+] to expand). The pre-release .7z file is a portable archive; extract it anywhere and run ioninja-5.5.1\bin\ioninja.exe.

Re "stop" of the analyzer -- could you share the original .njlog with the large packet?

I suspect that the reason is the incorrect handling of padding in the script (it went out of sync immediately after the large frame). Try removing the padding:

size_t frameSize =
	sizeof(BacNetMsTpHdr) +  // header
	hdr.m_length +           // payload
	sizeof(uint16_t);        // CRC
	// (hdr.m_length & 1);      // padding <---

Also, it makes sense to wait and sync on \x55\xff before starting buffering the packet; this way, the parser will re-sync after synchronization is lost (this still can occasionally happen because RS485 does not guarantee lossless delivery).

P.S.
For simplicity, just sync on 0x55; that should be good enough.

In BacNetMsTpParser.parse(...), add this:

	while (p < end) {
		void const* p0 = p;

		// 0. sync on 0x55

		if (!m_buffer.m_size) {
			void const* preamble = memchr(p, 0x55, end - p);
			if (!preamble)
				break;

			p = preamble; // skip everything before the preamble
		}

		...

Re "stop" of the analyzer -- could you share the original .njlog with the large packet?

How do I send you the log that is out of sync. I'll try your fixes and report back. Also here are a couple frame types if you want to add them to the analyzer:

enum BacNetMsTcpFrameType: uint8_t {
	Token                       = 0,
	PollForMaster               = 1,
	PollForMasterReply          = 2,
	TestRequest                 = 3,
	TestResponse                = 4,
	BacNetDataExpectingReply    = 5,
	BacNetDataNotExpectingReply = 6,
	ReplyPostponed              = 7,
	ExtendedDataExpectingReply	= 32,
	ExtendedDataNotExpectingReply = 33,
	CustomFrame					= 254,
	NoFrame						= 255
}

@vladimir Removing the padding gives this trying to load the Analyzer:
Capture.JPG

Inserting the changes to BacNetMsTpParser.jnc(without the modification to the padding) it runs very well with an occasional issues toward the end of a large packet:
Capture.JPG

While using this analyzer should we have been running Settings->Log Engine->Binary Data->Binary Data Merge with a 3msec threshold. Should we still be using this ?

Just want to also add how nice it is to be able to see the packets.

Thanks.

How do I send you the log that is out of sync

Hit "Reply" (not "Post quick reply"); the rightmost button on the toolbar is "Upload File". 7-zip it first so that it has the .7z extension permitted by this forum.

Removing the padding gives this trying to load the Analyzer:

Most likely, you just commented out the padding code, but forgot to add a semicolon ; at the end of the statement. In Jancy, just like in C, declarations and expression statements must be terminated with a semicolon.

While using this analyzer should we have been running Settings->Log Engine->Binary Data->Binary Data Merge with a 3msec threshold.

This won't affect the analyzer, but I think it's completely unnecessary now. You will see packets boundaries anyway -- and not just boundaries, decoded header fields, too.

Most likely, you just commented out the padding code, but forgot to add a semicolon ;

Exactly correct as I had missed the '+' on the line above.

Traces look very good now as I will try to move to your latest code. Tried using the upload images(right most icon) to send a 7z zip file but I don't have privileges for it.

Capture.JPG

Tried using the upload images(right most icon) to send a 7z zip file but I don't have privileges for it.

Not "Upload image", "Upload file". Does your "Reply toolbar" have this button?

8537fcf3-5daa-4d0d-8b6e-dd4ba104fe29-image.png

@vladimir That icon does not show on my Firefox. I'll try a different browser.
Capture.JPG

Apparently, this was a permission issue; it doesn't apply to administrators, so I didn't even know it was there...

Anyway, I've adjusted the file upload permissions for registered users; please try again.

@vladimir Sorry for the delay. Upload symbol is there but I get an error that file is too big. The log is 1Mbyte in size. Is there a way to reduce the size once the log is loaded into IO Ninja?

Thanks.

[0_1709752179535_BACnet_38400_heavery_traffic_error_v4.njlog](Uploading 100%)

No prob, I've increased the upload file size limit to 8MB.

Also, you could have archived it with 7z 😉

@vladimir
Here is a log that is working very well until time stamp 9:55:07 +00:46.470 and then the decode stops.

Thanks again for all the help.

BACnet_38400_heavery_traffic_error_v4.7z

Try the updated analyzer:

BacNetMsTp.7z

It checks CRCs now and discards frames with broken headers (in the previous log, the decoder didn't actually stop, but because a broken header specified a very long payload size, it kept buffering data assuming it still was a payload).

Also, when you upload the log, please detach the Analyzer before saving the log. This way, we can access the original raw & unprocessed data (instead of the already decoded frames).

@vladimir Looks really good. I'm going to send this log over to our BACnet stack supplier to see if he has any extra feedback. To me this looks much better than running a 485 to USB connection into WireShark.

Thanks again for all the help. Any chance you could release this into your standard layer plugins?

BACnet_38400_heavery_traffic_error_v5.7z