Skip to content
/ bacnet Public

A native Go implementation of the BACnet/IP protocol

License

Notifications You must be signed in to change notification settings

ulbios/bacnet

Repository files navigation

BACnet

BACnet implementation in pure Golang. This work was initially based on @kazukiigeta's work available on the kazukiigeta/bacnet repository.

Please bear in mind we only support IP as the BACnet transport layer for now.

We began working with the marshalling and unmarshalling routines defined in the original project and added a set of new messages. These are exposed through New*() functions defined on encoding.go and are parsed with the Parse() function defined on parsing.go.

In order to make adding new messages easier, we restructured the project and broke everything up in several directories:

  1. plumbing/: Everything related to BVLV, NPDU and APDU marshalling and unmarshalling.
  2. objects/: Definition of different BACnet objects so that they can be reused.
  3. services/: Implementation of several BACnet services such as ReadProperty and WriteProperty.
  4. common/: Utilities and definitions used across all the above.

On top of the BACnet implementation, we also offer a CLI-based program offering a way to test every available service. All the sources are contained on examples/. The binary can be generated with:

# Getting to the directory.
$ cd examples

# Time to build it!
$ go build

As always, you can alter the generated binary with environment flags and the like. We also provide a Taskfile you can leverage with task:

# Getting to the directory.
$ cd examples/

# Compile for Linux-based distros.
$ task

# Compile for macOS.
$ task build-mac

You can find more information on Task on its official site. Once compiled, just invoke the binary with no arguments to get an idea of what it can do. You can also take a look at its implementation: it's a good example of how to use the library on your own project!

Running the examples

In order to check our implementation is compliant, we interact with the server implementation offered by the bacnet-stack/bacnet-stack project. We have added it as a submodule to this repo so that you can easily get a copy to then compile the server.

In order to initialise the submodule you can either clone this repository with the --recursive flag or you can run git submodule update --init --recursive after you clone it: up to you!

Compiling the example applications is a matter of navigating to the correct directory and then running make:

# Navigate to the apps directory.
$ cd bacnet-stack/apps

# Time to compile!
$ make

If you do not have make installed, please refer to your system's package manager: it should be fairly easy to get it up and running. Once that's done, we can just run the examples.

Configuration

The bacnet-stack project's been written in pure C. In a fairly common C style, some of the configuration is done through environment variables. Please refer to the documentation at bacnet-stack/doc/README.* so that you know what to expect: for instance bacnet-stack/doc/README..utils contains information on how to configure the network interface the bacnet-stack tools will bind to.

The following examples have been executed leveraging the Vagrant escenario defined in the Vagrantfile. We'll make it clear from where (i.e. which machine) we're running things. Also, bear in mind you'll need to find the name of the bridge interface providing Vagrant's network backbone. On macOS that can be accomplished with ifconfig -a and on Linux the same's possible with ip a. You can also use WireShark to check what interfaces have traffic... At any rate, the names in our examples may (and very possibly will) differ from yours. Another easy way to identify the interface is to check its associated IPv4 address belongs to the 10.0.123.0/24 subnet, which is defined within the Vagrantfile. Bear in mind you must change this setting if it conflicts with existing subnets on your machine.

The purpose of running these examples is both making sure our tools work and capturing traffic to check it's compliant with WireShark's BACnet dissector.

Testing bacnet-stack server

From macOS we just need to start the server with Device ID 123 and Device Name foo with:

# Getting to the server directory.
$ cd bacnet-stack/apps/server

# Exporting the interface we want the server to bind to.
$ export BACNET_IFACE=bridge100

# Running it! You can use `--help` for more information.
$ ./bacserv 123 foo

From the VM we just need to run some clients:

# Getting to the apps directory.
$ cd bacnet-stack/apps

# The interface name on this machine's different!
$ export BACNET_IFACE=enp0s8

# Writing a property to Device ID 123. The MAC IPv4 address is the one assigned to the
# host machine on the Vagrant subnet and the port is BACnet's default one.
./writeprop/bacwp 123 analog-output 0 present-value 16 -1 4 1.1 --mac 10.0.123.1:47808

# Reading a property from Device ID 123. In this case, just like before, you can use the
# --help flag to get some more information on what you can use and what's configurable.
./readprop/bacrp 123 analog-output 1 present-value --mac 10.0.123.1:47808

# Making a WhoIs request.
./bacwi --mac 10.0.123.1:47808

Do bear in mind you can find the predefined devices and mock data on bacnet-stack's server on bacnet-stack/apps/server/epics_vts3.tpi.

Testing our ReadProperty server

From macOS we can just run:

# Go to the examples directory
$ cd examples

# Run the example ReadProperty server
$ ./bin/bexamples.osx.ex --remote-address 10.0.123.255:47808 rp

From the VM we can just query for some data:

# navigate to the ReadProperty directory
$ cd bacnet-stack/apps/readprop

# Query Device ID 321 for analog-output 2 and point it to the host's
# IPv4 address (i.e. 10.0.123.1).
$ ./bacrp 321 analog-output 2 present-value --mac 10.0.123.1:47808

Our ReadProperty server will answer requests for analog-outputs 0 and 1 and will reply with an Error otherwise.

With that, you can get the gist of what's going on: happy tweaking!

Traffic captures

In order to develop parsing and encoding functions we have captured traffic generated by the bacnet-stack implementation with WireShark. We have stored those capture files on traffcaps so that you can also leverage them. You should be able to open them up with WireShark without too much trouble. We will also store traffic captures generated with out examples to showcase how we are also compliant with the dissector.

About

A native Go implementation of the BACnet/IP protocol

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Languages