GNU/Linux - How to monitor bandwidth usage by SNMP with Munin

Munin logo Munin graph

Whether it is for the internet or locals links it can be interesting to have a graphical representation of the bandwidth usage over time.

We will see here how to proceed using the SNMP protocol coupled with Munin.

From wikipedia : Munin is a free and open-source computer system monitoring, network monitoring and infrastructure monitoring software application.

It comes with its own plugins and tools (to monitor : cpu, memory, network interfaces, disks…)

Unfortunately I couldn't find plugin that met my needs, so I decided do develop my own one.

Am I willing to share it here for free? This is what you'll find out by reading this article…

Network Architecture

In my example I want to monitor the WAN link of my router.

Munin architecture to monitor bandwidth WAN via SNMP
Munin Bandwidth Monitor.

SNMP

Intro

Simple Network Management Protocol is an Internet Standard protocol for collecting and organizing information about managed devices on IP networks and for modifying that information to change device behaviour. Devices that typically support SNMP include cable modems, routers, switches, servers, workstations, printers, and more.

As a reminder we can use SNMP by many ways : snmp get to retrieve information, snmp set to drive a device and snmp trap which allow an agent to send notifications to a manager

We will use snmp get here with SNMPv3 which is the most secure version of the protocol which allow cryptographic security.

So I will configure my router to use SNMPv3 with AES for encryption protocol and SHA1 as authentication protocol.

MIB

  • SNMP informations are stored in a tree called MIB. Here to retrieve network informations we need to search inside the (1.3.6.1.2.1.2) subtree :
The SNMP MIB tree to network interfaces
MIB tree.

Net-SNMP applications

There are many tools to interact with the snmp protocol. I personally use the net-snmp tools.

  • It gives us access to several cli tools :
    • snmpwalk : to retrieve a subtree of management values using SNMP GETNEXT requests
    • snmpget : to communicate with a network entity using SNMP GET requests
    • etc…

Installation

  • Install on Debian :
root@host:~# apt update && apt-get install snmp

Usage

  • Options :
    • -On : Displays the OID numerically
    • -Oe : Removes the symbolic labels from enumeration values
    • -OQ : Removes the type information when displaying varbind values
    • -Os : Display the MIB object name
    • -t 10 : Specifies the timeout in seconds between retries.
    • -l authPriv : Set the securityLevel used for SNMPv3 messages
    • -v 3 : Specifies the SNMP protocol version 3
snmpwalk
  • snmpwalk to retrieve all informations of subtree :
root@host:~# snmpwalk -OQse -v 3 -t 10 -l AuthPriv -u USER -a SHA1 -A AUTH_PASSWORD -x AES -X ENCRYPT_PASSWORD 192.168.1.254 1.3.6.1.2.1.2
iso.3.6.1.2.1.2.2.1.2.1 = "LAN"
iso.3.6.1.2.1.2.2.1.2.2 = "WAN"
iso.3.6.1.2.1.2.2.1.2.7 = "Resrved"
[…]
iso.3.6.1.2.1.2.2.1.10.1 = 993231759
iso.3.6.1.2.1.2.2.1.10.2 = 44681268
iso.3.6.1.2.1.2.2.1.11.1 = 21739
iso.3.6.1.2.1.2.2.1.12.1 = 657070
[…]
iso.3.6.1.2.1.2.2.1.15.1 = 239874
iso.3.6.1.2.1.2.2.1.16.1 = 3744831080
iso.3.6.1.2.1.2.2.1.16.2 = 671461169
[…]
iso.3.6.1.2.1.2.2.1.17.1 = 12892
iso.3.6.1.2.1.2.2.1.17.2 = 47095

As we can see it is hard to know which informations we have to process. To translate received information to human readable text we need to install snmp-mibs-downloader package which contain MIB files.

  • Edit /etc/apt/sources.list and add :
deb http://deb.debian.org/debian/ bullseye main non-free
deb-src http://deb.debian.org/debian/ bullseye main non-free
  • Install snmp-mibs-downloader :
root@host:~# apt update && apt install snmp-mibs-downloader
  • Edit /etc/snmp/snmp.conf :
# As the snmp packages come without MIB files due to license reasons, loading
# of MIBs is disabled by default. If you added the MIBs you can reenable
# loading them by commenting out the following line.
#mibs :
mibs ALL
# If you want to globally change where snmp libraries, commands and daemons
# look for MIBS, change the line below. Note you can set this for individual
# tools with the -M option or MIBDIRS environment variable.
#
# mibdirs /usr/share/snmp/mibs:/usr/share/snmp/mibs/iana:/usr/share/snmp/mibs/ietf
  • Run snmpwalk again, and now we have something understandable :
root@host:~# snmpwalk -OQse -v 3 -t 10 -l AuthPriv -u USER -a SHA1 -A AUTH_PASSWORD -x AES -X ENCRYPT_PASSWORD 192.168.1.254 1.3.6.1.2.1.2
ifDescr.1 = LAN
ifDescr.2 = WAN
ifDescr.7 = Resrved
[…]
ifInOctets.1 = 1247598139
ifInOctets.2 = 4105313719
ifInUcastPkts.1 = 4751
ifInNUcastPkts.1 = 146063
[…]
ifInUnknownProtos.1 = 61144
ifOutOctets.1 = 3285593712
ifOutOctets.2 = 2913538784
[…]
ifOutUcastPkts.1 = 2037
ifOutUcastPkts.2 = 13573

Now we can say that we will have to work with ifInOctets.2 (1.3.6.1.2.1.2.2.1.10.2) and ifOutOctets.2 (1.3.6.1.2.1.2.2.1.16.2).

snmpget
  • Example with snmpget :
root@host:~# snmpget -OQse -v 3 -t 10 -l AuthPriv -u USER -a SHA1 -A AUTH_PASSWORD -x AES -X ENCRYPT_PASSWORD 192.168.1.254 1.3.6.1.2.1.2.2.1.10.1
ifInOctets.1 = 854863486

Munin

Install

Apache web server

root@host:~# apt update && apt install apache2 libapache2-mod-fcgid
root@host:~# a2enmod fcgid

Munin

  • Install Munin packages :
root@host:~# apt update && apt install munin munin-node munin-plugins-extra
  • Enable munin Apache configuration :
root@host:~# a2enconf munin
  • Edit /etc/apache2/conf-enabled/munin.conf :
ScriptAlias /munin-cgi/munin-cgi-graph /usr/lib/munin/cgi/munin-cgi-graph
Alias /munin/static/ /var/cache/munin/www/static/

<Directory /var/cache/munin/www>
    Require all granted
    Options None
</Directory>

<Directory /usr/lib/munin/cgi>
    Require all granted
    <IfModule mod_fcgid.c>
        SetHandler fcgid-script
    </IfModule>
    <IfModule !mod_fcgid.c>
        SetHandler cgi-script
    </IfModule>
</Directory>


# ***** SETTINGS FOR CGI/CRON STRATEGIES *****

# pick _one_ of the following lines depending on your "html_strategy"
# html_strategy: cron (default)
Alias /munin /var/cache/munin/www
  • Restart Apache service :
root@host:~# systemctl restart apache2

Bandwidth Plugin

  • Install prerequisites :
root@host:~# apt update && apt install bc snmp
  • Create the file /usr/share/munin/plugins/bandwidth by ⚠️ taking care to modify the following variables ⚠️ :
    • AUTH_PASSWORD : SHA password
    • ENCRYPT_PASSWORD : AES password
    • HOST : ip address of your network device
    • OID_UPLOAD : oid of the upload link
    • OID_DOWNLOAD : oid of the download link
#!/bin/sh
# Role : munin script to collect upload and download values of an interface
# Autor : http://shebangthedolphins.net/
# 1.0 first version

AUTH_PASSWORD="AUTH_PASSWORD"
ENCRYPT_PASSWORD="ENCRYPT_PASSWORD"
HOST="192.168.1.254"
OID_UPLOAD="1.3.6.1.2.1.2.2.1.16.2"
OID_DOWNLOAD="1.3.6.1.2.1.2.2.1.10.2"

output_config() {
    echo "graph_category network"
    echo "graph_title Bandwidth"
    echo "plugins.label WAN Bandwidth"
    echo "graph_vlabel bytes"
    echo "wan1_upload.label upload"
    echo "wan1_download.label download"
}

output_values() {
    printf "wan1_upload.value %d\n" $(snmp_wan_upload) &
    printf "wan1_download.value %d\n" $(snmp_wan_download)
    wait
}

snmp_wan_upload() {
        TimeEnd_UL=$(date +"%s") #epoch time 
        if [ ! -f /tmp/wan_upload ]; then sleep 5s; snmpget -OQne -v 3 -t 10 -l AuthPriv -u USER -a SHA1 -A "$AUTH_PASSWORD" -x AES -X "$ENCRYPT_PASSWORD" "$HOST" "$OID_UPLOAD" | awk '{ print $3 }' > /tmp/wan_upload; sleep 5s; TimeEnd_UL=$(date +"%s"); fi #at first launch creation of the /tmp/wan_upload file
        ifInOctets_WAN1=$(cat /tmp/wan_upload) #value in bytes of the last reading (5 minutes ago)
        TimeStart_UL=$(date -r /tmp/wan_upload +"%s") #epoch time of the modification of the file /tmp/wan_upload 

        ifInOctets_WAN2=$(snmpget -OQne -v 3 -t 10 -l AuthPriv -u USER -a SHA1 -A $AUTH_PASSWORD -x AES -X $ENCRYPT_PASSWORD "$HOST" "$OID_UPLOAD" | awk '{ print $3 }' | tee /tmp/wan_upload) #current reading (+5 minutes compared to the previous reading)
        Time_UL=$(($TimeEnd_UL-$TimeStart_UL)) #difference in seconds between the current epoch time and that of the /tmp/wan_upload file
        Result_UL=$(echo "scale=0;($ifInOctets_WAN2 - $ifInOctets_WAN1)/($Time_UL)" | bc) #difference in bytes between the -5min record and the current record
        if [ $Result_UL -gt 0 ]; then #if difference is less than 0 then normal operation
                echo "scale=0;($ifInOctets_WAN2 - $ifInOctets_WAN1)/($Time_UL)" | bc
        else #if difference is less than 0 then the counter has been reset after the value 4294967295, the counter is reset to 0
                echo "scale=0;($ifInOctets_WAN2 - $ifInOctets_WAN1 + 4294967295)/($Time_UL)" | bc
        fi
}
snmp_wan_download() {
        TimeEnd_DL=$(date +"%s") #epoch time 
        if [ ! -f /tmp/wan_download ]; then sleep 5s; snmpget -OQne -v 3 -t 10 -l AuthPriv -u USER -a SHA1 -A "$AUTH_PASSWORD" -x AES -X "$ENCRYPT_PASSWORD" "$HOST" "$OID_DOWNLOAD" | awk '{ print $3 }' > /tmp/wan_download; sleep 5s; TimeEnd_DL=$(date +"%s"); fi #at first launch creation of the file /tmp/wan_download
        ifOutOctets_WAN1=$(cat /tmp/wan_download) #value in bytes of the last reading (5 minutes ago)
        TimeStart_DL=$(date -r /tmp/wan_download +"%s") #epoch time of the modification of the file /tmp/wan_download

        ifOutOctets_WAN2=$(snmpget -OQne -v 3 -t 10 -l AuthPriv -u USER -a SHA1 -A "$AUTH_PASSWORD" -x AES -X "$ENCRYPT_PASSWORD" "$HOST" "$OID_DOWNLOAD" | awk '{ print $3 }' | tee /tmp/wan_download) #current reading (+5 minutes compared to the previous reading) 
        Time_DL=$(($TimeEnd_DL-$TimeStart_DL)) #difference in seconds between the current epoch time and that of the /tmp/wan_download file
        Result_DL=$(echo "scale=0;($ifOutOctets_WAN2 - $ifOutOctets_WAN1)/($Time_DL)" | bc) #difference in bytes between the -5min record and the current record
        if [ $Result_DL -gt 0 ]; then
                echo "scale=0;($ifOutOctets_WAN2 - $ifOutOctets_WAN1)/($Time_DL)" | bc
        else
                echo "scale=0;($ifOutOctets_WAN2 - $ifOutOctets_WAN1 + 4294967295)/($Time_DL)" | bc
        fi
}

output_usage() {
    printf >&2 "%s - munin plugin to graph bandwith links\n" ${0##*/}
    printf >&2 "Usage: %s [config]\n" ${0##*/}
}

case $# in
    0)  
        output_values
        ;;
    1)
        case $1 in
            config)
                output_config
                ;;
            *)  
                output_usage
                exit 1
                ;;
        esac
        ;;
    *)  
        output_usage
        exit 1
        ;;
esac
  • Add execute permission :
root@host:~# chmod +x /usr/share/munin/plugins/bandwidth
  • Create symbolic link :
root@host:~# ln -s /usr/share/munin/plugins/bandwidth /etc/munin/plugins/bandwidth
  • Edit /etc/munin/plugin-conf.d/munin-node file and add :
[bandwidth]
user root
  • Restart munin-node service :
root@host:~# systemctl restart munin-node

Connect to Web Interface

  • Take a ☕, wait 5 minutes, then connect to your Munin server http://192.168.1.100/munin/, and click to localdomain link, then to Bandwidth :
Munin Main menu
  • You should see Bandwidth graphs :
Munin Bandwidth graphs

Useful commands

  • Manually run a plugin :
root@host:~# munin-run bandwidth
  • Print currentplugins state :
root@host:~# /usr/sbin/munin-node-configure | grep bandwidth
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Contact :