Advanced SNMP monitoring, part one: Asuswrt routers (Merlin build)

Part II (FreeNAS): Advanced SNMP monitoring, part two: FreeNAS
Part III (pfSense): TBC

Note: the initial tutorial used the exec directive which has been deprecated from the Asuswrt firmware. For completeness purposes I’ve left also the initial values.

SNMP already provides some OIDs (such as 1/5/15 load, CPU time, etc); however, most of the items included in the router’s GUI (temperature, memory usage, CPU usage) are not available through SNMP.

Although 1/5/15 loads are available over SNMP, CPU usage is a rather different animal. Additional description of the two: https://en.wikipedia.org/wiki/Load_(computing)

In order to extend the depth of the info available, results of scripts could be passed to SNMP (by using an empty OID - started with .1.3.6.1.2.1.25.1.8) by loading them into snmpd.conf (from /jffs/configs).

Probably this works on other routers that are based on Linux kernel too (Tomato, dd-wrt, etc).

This topic doesn’t deal with setting up the router (or installation of Merlin build) or with the security aspects of enabling SNMP access over external access.

Use at own risk!!!

Requirements:

Step 1: Create snmpd.conf.add file in /jffs/configs

nano /jffs/configs/snmpd.conf.add

Populate the file with the links to the scripts used:

extend .1.3.6.1.2.1.25.1.8 temp /bin/sh /jffs/scripts/snmp/temp.sh
extend .1.3.6.1.2.1.25.1.9 dhcp /bin/sh /jffs/scripts/snmp/dhcp.sh
extend .1.3.6.1.2.1.25.1.10 connected /bin/sh /jffs/scripts/snmp/connected.sh
extend .1.3.6.1.2.1.25.1.11 chip1 /bin/sh /jffs/scripts/snmp/chip1.sh
extend .1.3.6.1.2.1.25.1.12 chip2 /bin/sh /jffs/scripts/snmp/chip2.sh
extend .1.3.6.1.2.1.25.1.13 mem /bin/sh /jffs/scripts/snmp/mem.sh
extend .1.3.6.1.2.1.25.1.14 idle /bin/sh /jffs/scripts/snmp/idle.sh
extend .1.3.6.1.2.1.25.1.15 uptime /bin/sh /jffs/scripts/snmp/uptime.sh
extend .1.3.6.1.2.1.25.1.16 jffs /bin/sh /jffs/scripts/snmp/jffs.sh

Step 2: create the scripts on the router with the following form:

nano /jffs/scripts/snmp/name_of_the_script.sh

  • processor temperature
    #!/bin/sh
    output=$(cat /proc/dmu/temperature); echo $output | cut -c19-20

  • number of active DHCP leases
    #!/bin/sh
    cat /var/lib/misc/dnsmasq.leases| wc -l

  • number of connected clients
    #!/bin/sh
    arp -a | awk '$4!="<incomplete>"' | wc -l

  • wireless chips temperature

  • Chip1 (2.4Ghz)
    #!/bin/sh
    wl -i eth1 phy_tempsense | awk '{print $1}'

  • Chip2 (5Ghz)
    #!/bin/sh
    wl -i eth2 phy_tempsense | awk '{print $1}'

  • Memory Used
    #!/bin/sh
    top -bn1 | head -3 | awk '/Mem/ {print $2}'

  • Idle processor (in HA the usage is computed by 100 - idle)
    #!/bin/sh
    top -bn1 | head -3 | awk '/CPU/ {print $8}'

  • Uptime
    #!/bin/sh
    uptime | cut -d ',' -f1 | sed 's/^.\{12\}//g'

  • JFFS (or other disk, just replace jffs with sdx) used space
    #!/bin/sh
    df | awk '/jffs/ {print $5}'

Step 3: create Home Assistant sensors

- platform: snmp
  host: ip.of.your.router
  baseoid: 1.3.6.1.2.1.25.1.8.3.1.1.4.116.101.109.112 #previous (with exec directive) 1.3.6.1.2.1.25.1.8.101.1
  name: router_temp
  accept_errors: true
  default_value: 0
  value_template: '{{value | float  | round (0) }}'
  version: 2c
  unit_of_measurement: '°C'
  scan_interval: 35
- platform: snmp
  host: ip.of.your.router
  baseoid: 1.3.6.1.2.1.25.1.9.3.1.1.4.100.104.99.112 #previous (with exec directive) 1.3.6.1.2.1.25.1.9.101.1
  name: router_dhcp_leases
  accept_errors: true
  default_value: 0
  value_template: '{{value | float  | round (0) }}'
  version: 2c
  unit_of_measurement: ' '
  scan_interval: 35
- platform: snmp
  host: ip.of.your.router
  baseoid: 1.3.6.1.2.1.25.1.10.3.1.1.9.99.111.110.110.101.99.116.101.100 #previous (with exec directive) 1.3.6.1.2.1.25.1.10.101.1
  name: router_connected
  accept_errors: true
  default_value: 0
  value_template: '{{value | float  | round (0) }}'
  version: 2c
  unit_of_measurement: ' '
  scan_interval: 35
- platform: snmp
  host: ip.of.your.router
  baseoid: 1.3.6.1.2.1.25.1.11.3.1.1.5.99.104.105.112.49 #previous (with exec directive) 1.3.6.1.2.1.25.1.11.101.1
  name: router_24_temp
  accept_errors: true
  default_value: 0
  value_template: '{{value | float  | round (0) }}'
  version: 2c
  unit_of_measurement: '°C'
  scan_interval: 35
- platform: snmp
  host: ip.of.your.router
  baseoid: 1.3.6.1.2.1.25.1.12.3.1.1.5.99.104.105.112.50 #previous (with exec directive) 1.3.6.1.2.1.25.1.12.101.1
  name: router_5_temp
  accept_errors: true
  default_value: 0
  value_template: '{{value | float  | round (0) }}'
  version: 2c
  unit_of_measurement: '°C'
  scan_interval: 35
- platform: snmp
  host: ip.of.your.router
  baseoid: 1.3.6.1.2.1.25.1.13.3.1.1.3.109.101.109 #previous (with exec directive) 1.3.6.1.2.1.25.1.13.101.1
  name: router_mem
  accept_errors: true
  default_value: 0
  value_template: '{{((value | replace("K","") | float ) /5000 | float ) | round (0) }}'
  version: 2c
  unit_of_measurement: '%'
  scan_interval: 35
- platform: snmp
  host: ip.of.your.router
  baseoid: 1.3.6.1.2.1.25.1.14.3.1.1.4.105.100.108.101 #previous (with exec directive) 1.3.6.1.2.1.25.1.14.101.1
  name: router_proc
  accept_errors: true
  default_value: 0
  version: 2c
  value_template: '{{( 100 - (value  | replace("%",""))  | float  )| round (2) }}'
  unit_of_measurement: '%'
  scan_interval: 35

Step 4: Customize sensors (icons, names, grouping etc)

Bonus: Lovelace card (most of the info from the router’s webpage and also switches for blocking internet access: Internet kill switch (for both wireless & wired devices)

card:
  cards:
    - content: >
        [Web access Router](http://192.168.0.1)  Uptime:
        {{states.sensor.ac88u_uptime.state}}
      type: markdown
    - cards:
        - entity: sensor.ac88u_dhcp_leases
          title: Leases
          type: 'custom:bignumber-card'
        - entity: sensor.ac88u_connected
          title: Connected
          type: 'custom:bignumber-card'
      type: horizontal-stack
    - entities:
        - sensor.wan_traffic_in_2
        - sensor.wan_traffic_out_2
      type: history-graph
    - cards:
        - entity: sensor.ac88u_proc
          severity:
            green: 50
            red: 90
            yellow: 80
          type: gauge
        - entity: sensor.ac88u_temp
          max: 120
          min: 60
          severity:
            green: 80
            red: 120
            yellow: 90
          type: gauge
      type: horizontal-stack
    - bar_style: null
      entity: sensor.ac88u_15min_load
      height: 35 px
      max: 2
      min: 0
      padding-left: 2px
      severity:
        - color: seagreen
          value: 0.7
        - color: orange
          value: 0.8
        - color: red
          value: 1.5
      text-align: left
      title: Processsor load
      title_position: inside
      type: 'custom:bar-card'
    - entities:
        - entity: sensor.ac88u_15min_load
        - entity: sensor.ac88u_1min_load
      type: history-graph
    - bar_style: null
      entity: sensor.ac88u_mem
      height: 35 px
      max: 100
      min: 0
      padding-left: 2px
      severity:
        - color: seagreen
          value: 70
        - color: orange
          value: 80
        - color: red
          value: 96
      text-align: left
      title: Memory use
      title_position: inside
      type: 'custom:bar-card'
    - entities:
        - sensor.ac88u_mem
      type: history-graph
    - entities:
        - entity: switch.receiver
        - entity: switch.xbox_one
        - entity: switch.dvd
      show_header_toggle: false
      title: Block internet access
      type: entities
  title: AC88u
  type: 'custom:vertical-stack-in-card'
type: 'custom:card-loader'
wait:
  - bar-card
  - bignumber-card
  - vertical-stack-in-card
6 Likes

Thanks for sharing this. I will be using it.

Related question that I could never fully solve (I have an AC3100 running asus merlin) - there is a known issue with bandwidth reporting via snmp that affects most asus routers currently:

it is a linux kernel issue. Most use an outdated 2.6 series kernel that has the problem. At some point in the 4.x series this got fixed however almost all current asus routers are stuck on 2.6. It can be mitigated somewhat as posted elsewhere but no true fix.

Somehow the Web-UI of the router is not impacted by this - it probably pulls the byte counter values from the driver rather than from the kernel (like snmpd does). So I just thought I could “fix” the snmp bandwidth reporting issues by creating OIDs with the method you describe here and scripts that query the driver directly. Did you by any chance run into this issue / found a solution for it?

Hi,

Unfortunately I didn’t found a way of getting bandwidth data from the Asus router for the reasons you mentioned. I recall reading your posts on SNB forum :slight_smile: I got stuck at getting a function call in the firmware to be able then to run it in terminal and didn’t really put a lot of effort in.

Instead, I use SNMP on a Celeron J1900 pfsense box (it delivers adblocker, antivirus and IDS/IPS with loads between 0.3 and 0.8 which is great on a quadcore system) sitting in front of Asus to get the values needed for computing the bandwidth alongside other sensor data. The values for bandwidth I have in HA from pfsense are pretty close to the ones on Asus webpage.

Will try again when I’ll switch to a Wifi 6 router as I guess it’s going to have kernel 5 :slight_smile:

Interesting workaround :slight_smile:
Also expecting to upgrade the router at some point - after 802.11ax + WPA3 goes mainstream. It may take a while though and the 3100 is still a great device

Hey Petrica, tnx for the tutorial.
I had some issues and maybe other people have the same.

  1. for the baseoids that end in .101.1 do not work for me and i used

snmpwalk -Os -c public -v2c 192.168.1.1 (oid from snmpd.conf.add)

and got the correct OID for me.

  1. router_uptime returns " X min" which can’t be converted to float.
  2. router_jffs should replace with

value_template: ‘{{ (value | replace("%","")) | float | round (0) }}’

Hi,

  1. Thanks for the heads up. I think the initial version of the tutorial was based on exec directive which was phased out subsequently leaving only extend but haven’t got the time to update this tutorial (this creates a different structure for the OIDs).

I will soon update it with the new values and also add tutorials for custom SNMP monitoring for FreeNAS and pfSense.

  1. You can use a standard OID to get it in a verbose form:
- platform: snmp
  host: 192.168.0.1
  baseoid: 1.3.6.1.2.1.25.1.1.0
  name: 'AC88u Uptime'
  value_template: >-
    {% set time = (value | int) | int %}
    {% set minutes = ((time % 360000) / 6000) | int%}
    {% set hours = ((time % 8640000) / 360000) | int %}
    {% set days = (time / 8640000) | int %}
      {%- if time < 60 -%}
        Less then 1 min
        {%- else -%}
        {%- if days > 0 -%}
          {{ days }}d
        {%- endif -%}
        {%- if hours > 0 -%}
          {%- if days > 0 -%}
            {{ ' ' }}
          {%- endif -%}
          {{ hours }}hr
        {%- endif -%}
        {%- if minutes > 0 -%}
          {%- if days > 0 or hours > 0 -%}
            {{ ' ' }}
          {%- endif -%}
          {{ minutes }}min
        {%- endif -%}
      {%- endif -%}
  1. Done
3 Likes

Tnx for the quick update

Hello,

Thank’s for this tutorial but didn’t works on my side :frowning:

Someone can use custom scripts/custom snmpd conf on Fresh Tomato?

I just try to add the scripts & snmpd.conf.add -> restart snmpd & doesn’t works :frowning:

  • Native OID (MaxProcess=OK):

snmpget -v 2c -c rocommunity 10.0.0.1 iso.3.6.1.2.1.25.1.6.0
iso.3.6.1.2.1.25.1.6.0 = Gauge32: 47

  • Custom OID (CPU temp=FAIL):

snmpget -v 2c -c XXXX XXXX iso.3.6.1.2.1.25.1.8.0
iso.3.6.1.2.1.25.1.8.0 = No Such Object available on this agent at this OID

Jffs is enabled but the directory is empty (same if I try disable/enable/format) so I created manually required directories.

Script content:

$ cat /jffs/scripts/snmp/temp.sh
#!/bin/sh
output=$(cat /proc/dmu/temperature); echo $output | cut -c19-20

Snmpd conf custom content:

$ cat /jffs/configs/snmpd.conf.add
extend .1.3.6.1.2.1.25.1.8 temp /bin/sh /jffs/scripts/snmp/temp.sh

Script executable:

$ /jffs/scripts/snmp/temp.sh
58

Best regards,

Hi,

I’ve only used a Tomato router for a brief period of time, moving to Asuswrt (which is a commercial router firmware based on the Tomato). As always when dealing with custom firmware, proceed at your own risk!

jffs is only used to store user defined scripts and settings (so it would be empty on the first run; in Asuswrt other stuff like user defined icons and OpenVPN certificates have been moved here).
In Tomato GUI you would need to input the name of the script to be executed that will load the results of the other scripts into SNMP (/jffs/configs/snmpd.conf.add) in Administration->JFFS->Execute When Mounted (see emulator in the link below).

http://victek.is-a-geek.com/virtual/tomatok26/admin-jffs2.html

The other scripts (temp.sh, dhcp.sh, etc) would be loaded by snmp.conf.add

I’ve used initially exec to pass values to SNMP however it has been deprecated in Asuswrt firmware. I’ve replaced the tutorial with extend directive and the updated version reflects this.

Thank you for your anwser!

But I don’t understand. Indeed, If I add /jffs/configs/snmpd.conf.add (emulator seems read only) in the “Execute When Mounted”, the boot will fail no? Because the snmpd.conf.add file is not a script but a config file (for snmpd)

Best regards,

I don’t know if boot will fail (probably not) but I suggest not risking it.

Although Asuswrt is based on Tomato, it was forked a long time ago and there are probably lots of different features by now. While Fresh Tomato is the sole version of Tomato actively developed it would be difficult to know if any settings are conflicting without extensive documentation.

Hello!
Thanks for the good guides.
For a long time I could not understand why this does not work for me.
The reason is that In the firmware from Merlin Extend support was only added with 384.8. In my router, the firmware is earlier.
Maybe this will help someone.

I started looking into this when the home-assistant asuswrt integration broke in .107

i got some clues how to calculate crude bandwidth from this url


specifically this script “router_net.sh”

in a nutshell my wan interface is eth0

running either of the two linux commands on asus merlin RT-AC5300
will give you the same value, the cumulative bytes received

      cat /proc/net/dev |grep eth0 |awk '{print $2}'
or 
      cat /sys/class/net/eth0/statistics/rx_bytes

running either of the two linux commands
will give you the same value, the cumulative bytes transmitted

       cat /proc/net/dev | grep eth0 | awk '{print $10}'
or
       cat /sys/class/net/eth0/statistics/tx_bytes

it looks like assuwrt integration is polling a very similar value every 30 seconds
and subtracting the 2 consecutive cumulative values then multiplied by 8 then roughly divided by time interval, delta time between the polls

they did have an additional logic, if the counter rolls over, causing the difference to be negative, they add max integer value 4294967295.

i’ve done speed tests, with my changes in configuration.yaml, automation.yaml my sensor values increase similarly to the asuswrt integration sensor.

asuswrt integration is fixed now.

1 Like

Hi,

thanks for the great guide. I stumbled upon this during a google crawl, and although I don’t use the home-assistant it was still very helpful to me from monitoring my Asus router.
I did have one issue though:
The number of connected clients didn’t really work as the arp command shows all devices found on the local network that have been talking to the router. This isn’t necessarily the same as the number of connected clients. Although I don’t really have a way to accurately count the number of connected clients through the ethernet ports, I do have a way to do that for the Wifi clients.

In my config I changed the connected.sh script to:

#!/bin/sh
( wl -i eth1 assoclist; wl -i eth2 assoclist )|wc -l

This will add up all the clients connected to the 2.4ghz (eth1) and 5ghz (eth2) wifi.

I also have a way to count the number of ethernet ports in use, but as there might be a switch connected to one of the ports (and thus more clients), this isn’t really accurately counting the number of connected clients. If someone has another idea on how to do this I’d like to hear it.

Number of connected wired clients:

#!/bin/sh
robocfg show |grep "Port [1234]:.*10.*enabled " |wc -l

robocfg outputs 8 ports in my case (I only have 4 switchports on my router): 0 is the WAN port, 5 is (I think) connected to the bridge interface, 7 and 8 are not in use. So I only check ports 1-4 for the text “10 enabled”. If nothing is connected the output is: “Port x: DOWN enabled” instead of “Port x: <speed: 10,100,1000FD> enabled”

It’s not pretty, but works :slight_smile:

2 Likes

Hi does anyone have this working on an Asus rt86ac running the latest Merlin fw? Any issues to look for?

I’ve tested on AC87u and AC88u but I don’t know if there are issues with AC86u (more recent than the other two).

That’s ok if you on recent firmware like 364.16+ then I’m good to go. Currently on .13 and was going to do a scratch install to .19 and do this at the same time

I am running AC66U with Merlin (380.69_2).
FW is not the latest since in later versions there are problems with DualWAN.

So, what I did is:

  1. Put .sh files into /jffs (all files are as per the 1st post).

  2. Changed attributes: chmod a+rx *.sh

  3. Created snmpd.conf.add (as per the 1st post):

extend .1.3.6.1.2.1.25.1.8 temp /bin/sh /jffs/scripts/snmp/temp.sh
extend .1.3.6.1.2.1.25.1.9 dhcp /bin/sh /jffs/scripts/snmp/dhcp.sh
extend .1.3.6.1.2.1.25.1.10 connected /bin/sh /jffs/scripts/snmp/connected.sh
extend .1.3.6.1.2.1.25.1.11 chip1 /bin/sh /jffs/scripts/snmp/chip1.sh
extend .1.3.6.1.2.1.25.1.12 chip2 /bin/sh /jffs/scripts/snmp/chip2.sh
extend .1.3.6.1.2.1.25.1.13 mem /bin/sh /jffs/scripts/snmp/mem.sh
extend .1.3.6.1.2.1.25.1.14 idle /bin/sh /jffs/scripts/snmp/idle.sh
extend .1.3.6.1.2.1.25.1.15 uptime /bin/sh /jffs/scripts/snmp/uptime.sh
extend .1.3.6.1.2.1.25.1.16 jffs /bin/sh /jffs/scripts/snmp/jffs.sh
  1. In the router log there are lines on bootup:
Nov 19 23:25:57 rc_service: httpd 312:notify_rc restart_snmpd
Nov 19 23:25:57 custom_config: Appending content of /jffs/configs/snmpd.conf.add.
  1. The content of /tmp/snmtpd.conf:
agentAddress  udp:161
createUser XXXXXXXXXXXX
rwuser XXXXXXXXXXXX noauth
rocommunity fortress default
sysName AC66U
sysLocation Home
extend .1.3.6.1.2.1.25.1.8 temp /bin/sh /jffs/scripts/snmp/temp.sh
extend .1.3.6.1.2.1.25.1.9 dhcp /bin/sh /jffs/scripts/snmp/dhcp.sh
extend .1.3.6.1.2.1.25.1.10 connected /bin/sh /jffs/scripts/snmp/connected.sh
extend .1.3.6.1.2.1.25.1.11 chip1 /bin/sh /jffs/scripts/snmp/chip1.sh
extend .1.3.6.1.2.1.25.1.12 chip2 /bin/sh /jffs/scripts/snmp/chip2.sh
extend .1.3.6.1.2.1.25.1.13 mem /bin/sh /jffs/scripts/snmp/mem.sh
extend .1.3.6.1.2.1.25.1.14 idle /bin/sh /jffs/scripts/snmp/idle.sh
extend .1.3.6.1.2.1.25.1.15 uptime /bin/sh /jffs/scripts/snmp/uptime.sh
extend .1.3.6.1.2.1.25.1.16 jffs /bin/sh /jffs/scripts/snmp/jffs.sh

So it seems that lines are properly added to the conf file.

But - the added OIDs are not displayed with snmpwalk:

PS H:\Trash\snmp> .\SnmpWalk.exe -r:XXXXXXXXXXXXXXX -c:fortress -csv -Os:1.3.6.1.2.1.25.1.
.1.3.6.1.2.1.25.1.1.0,TimeTicks,15:16:31.48
.1.3.6.1.2.1.25.1.2.0,OctetString,  07 E4 0B 14 00 22 22 00 2B 03 00
.1.3.6.1.2.1.25.1.3.0,Integer,393216
.1.3.6.1.2.1.25.1.4.0,OctetString,root=/dev/mtdblock3 console=ttyS0,115200 init=/sbin/preinit

.1.3.6.1.2.1.25.1.5.0,Gauge32,0
.1.3.6.1.2.1.25.1.7.0,Integer,0
.1.3.6.1.2.1.25.2.2.0,Integer,239524
.1.3.6.1.2.1.25.2.3.1.1.1,Integer,1
.1.3.6.1.2.1.25.2.3.1.1.3,Integer,3

What am I doing wrong?
Sorry, I am new to SNMP…

Hi,

Could you confirm that all scripts are actually outputting correct values?

For instance (run on the router): sh /jffs/scripts/snmp/temp.sh

If the script for the temperature doesn’t work (AFAIK, AC66u uses older gen firmware) could you try cat /sys/class/thermal/thermal_zone0/temp?

Good day, thanks a lot for your reply!

  1. I tried with standard “uptime” OID , it works:
  - platform: snmp
    host: !secret router_1_ip
    port: 161
    community: fortress
    baseoid: 1.3.6.1.2.1.25.1.1.0
    name: snmp_router_1_uptime_ticks
    accept_errors: true
    default_value: 0
    value_template: '{{ value }}'
    version: 2c
    unit_of_measurement: 'ticks'
    scan_interval: 300
  1. Output of scripts:
sh chip1.sh
69

sh chip2.sh
81

sh connected.sh
15

sh dhcp.sh
18

sh idle.sh
80.0%

sh jffs.sh
3%

sh mem.sh
84396K

sh temp.sh
cat: can't open '/proc/dmu/temperature': No such file or directory

sh uptime.sh
 10:19
  1. Regarding temperature:
cat /sys/class/thermal/thermal_zone0/temp
cat: can't open '/sys/class/thermal/thermal_zone0/temp': No such file or directory

It is my fault too - I forgot that CPU temperature is not supported in AC66U, see web gui (Merlin):

  1. What I did now for testing purpose:
    a) removed temp.sh;
    b) excluded a line with “temp.sh” from snmpd.conf.add (and changed OIDs correspondingly);
    c) rebooted the router;
    d) ran snmpwalk - still no added OIDs.