Cross-compiling collectd for ASUSWRT

I have been using collectd on my server to monitor traffic (inbound, outbound and to/from the Internet), as well as disk stats because it’s being used as a NAS. So far it has been helpful, observing various graphs to understand patterns, and detecting problems when they happen.

I’m also recording video from a WiFi camera, so I can constantly see traffic that comes into the server. But without visibility on the router itself, I am unable to determine whether the traffic is from the 5 GHz or 2.4 GHz band, or the guest network.

By getting a collectd instance onto the router, we can get those detailed interface statistics separately.

Cross-compiling collectd

In previous articles, I have just briefly said “use the compiler with the SDK”. Here, I’ll walkthrough which compiler to use and to solve the dependencies for the toolchain.

I’m doing this on a Ubuntu 18.04 machine. I chose the Server edition because it’s easier to breeze through the installer.

RMerl (maintainer of Asuswrt-Merlin) has extracted the compiler toolchains from the various ASUSWRT source packages and placed them in a convenient repository.

Download and unpack the toolchain:

wget -O am-toolchains.zip https://github.com/RMerl/am-toolchains/archive/master.zip

unzip am-toolchains.zip

Note that you may need to install unzip on a clean machine. This should unpack to a am-toolchains-master directory.

Installing dependencies for the compiler toolchain:

dpkg --add-architecture i386
apt update   # get new lists after adding arch

apt install {libmpc3,libstdc++6,libelf1}:i386

For the RT-AC68U model, the arm-brcm-linux-uclibcgnueabi compiler in the brcm-arm-sdk directory should be used. To get the compiler up and running, we need to fake certain old version of libraries by linking them to their newer versions 1:

cd /usr/lib/i386-linux-gnu
ln -s libmpc.so.3  libmpc.so.2 
ln -s libmpfr.so.6 libmpfr.so.4

Set up $PATH to point to the directory with the toolchain:

export PATH=$PATH:~/am-toolchains-master/brcm-arm-sdk/hndtools-arm-linux-2.6.36-uclibc-4.5.3/bin

Now you can proceed to download and compile collectd:

# install dependencies
apt install autoconf libtool pkg-config flex bison

git clone --branch collectd-5.8.1 --depth 1 https://github.com/collectd/collectd

autoreconf -i -v
./configure --host=arm-brcm-linux-uclibcgnueabi \
    --prefix=/opt \
    --runstatedir=/var/run \
    --with-fp-layout=nothing --disable-pcie_errors

make all

Note that you will need several other meta files, as I have outlined in my previous post. Those will help to automatically start the collectd process on boot. I won’t go through them again, but they are available for download below.

Gather the files to be installed into a tar.gz archive:

make install DESTDIR=/tmp/collectd
tar -C /tmp/collectd czvf collectd.tar.gz

I’m using tar.gz because there’s no ipkg installed on the router by default.

Installation on the Router

Copy the tar.gz archive onto the router. You can use either a USB drive or wget.

Unpack the archive onto the router USB drive, which is where apps are installed:

tar -C /tmp/mnt/DISK_IMG/asuswrt.arm/ xzvf collectd.tar.gz

The actual app directory and mountpoint can be inspected using the nvram command:

# nvram get apps_mounted_path
/tmp/mnt/DISK_IMG

# nvram get apps_install_folder
asusware.arm

Once installed, there is a ASUSWRT helper script that will do some app initialization (like copying the startup files into /opt, symlink-ing directories, etc). We can use that to start the collectd service like so:

# app_init_run.sh collectd start
collectd: script invoked with start...
collectd: starting collectd...
plugin_load: plugin "interface" successfully loaded.
plugin_load: plugin "network" successfully loaded.

As long as the control file says the service is enabled, it will come up automatically on the next reboot. You can check the “System Log” to verify if the service was successfully started.

Configuring collectd

I’m using a basic configuration to collect only interface statistics. The network plugin submits these collected stats to a master collectd server.

Hostname "asuswrt-1"
BaseDir "/tmp"

# network
Interval 20
LoadPlugin interface
<Plugin interface>
    Interface "eth1"
    Interface "eth2"
</Plugin>

LoadPlugin network
<Plugin network>
    Server "192.168.0.1"
</Plugin>

I’ve previously also collected CPU and load stats on the router, but they don’t seem to be very useful or indicative of anything.

I decided to add some simple substitution in the startup script that assists with filling in dynamic things like interface names. This means when you create a new guest network, you don’t really need to modify the collectd.conf file. The collectd service however will need to be restarted in order for it to pick up the new interface names (which I think the system does automatically).

To get the interface names on the LAN side (wireless + wired Ethernet), you can query it from nvram like so:

nvram get lan_ifnames

To intergrate this list into the config file, a scriptlet that builds on the above example is then inserted into the appropriate location:

<Plugin interface>
<? for a in `nvram get lan_ifaces`; do echo interface \"$1\"; done ?>
</Plugin>

This will add Interface config lines into the plugin section dynamically, making it easier to write a generic config file.

Stay Tuned…

The build script, together with other required files, are hosted on GitHub. The script is still a work-in-progress at the moment, but it is usable. I’ll be working towards getting it into a more polished state.

You should now be able to receive interface stats from this collectd instance running on the router, which will give you a detailed breakdown of the various wireless bands and/or guest networks.

In the next post, I’ll dive deeper into how we can collect more detailed information from the router.


  1. Not really recommended since the libraries might not be ABI compatible across versions, but hey, it worked. 

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.