Statystyki routera w OpenWRT (collectd, rrdtool)

Spis treści

Domowe routery WiFi chodzą zwykle 24 godziny na dobę. Ich moc obliczeniowa, choć zwykle niewielka, czasem się marnuje. Mając router z OpenWRT, możemy przerobić go tak, by zbierał różnego rodzaju dane dla statystyki. Te dane mogą pochodzić z różnych źródeł i nie koniecznie muszą one dotyczyć samego routera. Tego typu funkcjonalność mogą zapewnić nam narzędzia collectd oraz rrdtool . W tym artykule spróbujemy zaprogramować router, by zbierał pewne dane dotyczące połączenia sieciowego. Na podstawie tych informacji będą rysowane wykresy, które następnie będą udostępniane przez serwer uhttpd .

Instalacja potrzebnego oprogramowania

Jest wiele różnych narzędzi realizujących to przedsięwzięcie, za które się zabieramy. Jak wspomnieliśmy we wstępie, tutaj skupimy się głównie na collecd . To przy jego pomocy będziemy tworzyć bazy danych zawierające określone wartości. Drugim potrzebnym nam programem będzie rrdtool . Przy jego pomocy zaś będziemy wydobywać informacje z bazy i na ich podstawie generować zrozumiałe dla człowieka wykresy. Dodatkowo, by mieć łatwy dostęp do tak stworzonych obrazków, musimy postawić serwer www. Do tego celu znakomicie się nadaje uhttpd . Te fotki mogą być do wglądu nawet i spoza sieci domowej, niemniej jednak, my się ograniczymy tylko do sieci lokalnej. Instalujemy zatem potrzebne pakiety:

# opkg update
# opkg install \
    collectd \
    collectd-mod-rrdtool \
    rrdtool1 \
    uhttpd

Poza powyższymi pakietami, potrzebne nam są również dodatkowe moduły dla collectd. Jest ich dość sporo, a dokładną listę możemy uzyskać wpisując w terminalu polecenie opkg list | grep collectd-mod . Wybieramy te moduły, które według nas będą nam potrzebne i instalujemy. W tym przypadku są to te poniższe pakiety:

# opkg install \
    collectd-mod-conntrack \
    collectd-mod-cpu \
    collectd-mod-load \
    collectd-mod-memory \
    collectd-mod-ping

Konfiguracja uhttpd

Zacznijmy może od konfiguracji serwera www. Plik konfiguracyjny dla uhttpd znajduje się w /etc/config/uhttpd . Nie jest on zbytnio złożony. Interesują nas te poniższe pozycje:

#   list listen_http        0.0.0.0:80
#   list listen_http        [::]:80
    list listen_http        192.168.1.1:80

#   list listen_https       0.0.0.0:443
#   list listen_https       192.168.1.1:443
#   list listen_https       [::]:443

    option home             /tmp/router/www

Wszystkie widoczne wyżej linijki, które zaczynają się od znaku # oznaczają, że wpis został wykomentowany i nie będzie brany pod uwagę przez uhttpd . Chodzi generalnie o to, że nie potrzebujemy, by demon nasłuchiwał na wszystkich możliwych interfejsach. No i również zbędne nam jest połączenie SSL/TLS. W opcji listen_http ustawiliśmy za to adres interfejsu br-lan . Zmieniliśmy także katalog główny, w którym są przechowywane pliki stron www przy pomocy opcji home . Zapisujemy plik, tworzymy ręcznie w/w katalog i restartujemy demona poniższym poleceniem:

# mkdir -p /tmp/router/www/
# /etc/init.d/uhttpd restart

Konfiguracja collectd

Serwer www powinien nam już działać w tle. Teraz przechodzimy do collectd . Plik konfiguracyjny tego narzędzia znajduje się w /etc/collectd.conf . W zależności od zainstalowanych modułów, różne sekcje się w tym pliku będzie umieszczać. Niemniej jednak, na początku tego pliku definiujemy zachowanie samego demona collectd :

Hostname        "the-mountain"
#FQDNLookup     true
BaseDir         "/var/run/collectd"
#Include        "/etc/collectd/conf.d"
PIDFile         "/var/run/collectd.pid"
PluginDir       "/usr/lib/collectd"
TypesDB         "/usr/share/collectd/types.db"
Interval        10
ReadThreads     2

Chodzi głównie o dostosowanie interwału w parametrze Interval . Będzie on nam potrzeby w późniejszej fazie konfiguracji. W tym przypadku ustawiliśmy go na 10 sekund. Dostosujmy sobie także parametr Hostname , bo to ta nazwa będzie widoczna min. na wykresach.

Kolejne linijki w pliku /etc/collectd.conf dotyczą ładowania modułów. Wszystkie z tych modułów, które zainstalowaliśmy i chcemy aktywować, muszą zostać tutaj określone w opcji LoadPlugin Poniżej został włączony moduł ping :

LoadPlugin ping

Każdy z modułów ma specyficzne dla siebie opcje. Są one ujmowane w sekcje <Plugin nazwa> , gdzie nazwa określa załadowany moduł. Niestety nie da rady opisać ich tutaj wszystkich możliwych modułów. Niemniej jednak, na potrzeby tego artykułu muszą zostać opisane conntrack , cpu , load , memory , ping oraz rddtool .

Poniżej jest konfiguracja modułu ping . Ma on za zadanie odpytywać co pewien okres czasu hosty o adresach określonych w parametrach Host . Czas jest zdefiniowany w opcji Interval . Musi się on pokrywać z tym interwałem, który ustawiliśmy wcześniej. Częściej nie ma potrzeby odpytywać serwerów, bo i tak tylko jeden ping na 10 sekund zostanie zanotowany. Ping'owany host ma też skończony czas na przesłanie odpowiedzi, którą określa parametr Timeout . Jeśli w tym czasie nie zostanie udzielona odpowiedź, to próba zostanie oznaczona jako nieudana. Ważne jest też, by podać przy pomocy opcji Device interfejs, przez który będzie wychodził ping . W tym przypadku jest to interfejs WAN.

<Plugin ping>
    Host "8.8.8.8"
    Host "208.67.220.220"
    Host "212.77.100.101"
    Interval 10.0
    Timeout 2.0
    TTL 64
    Device "eth0.2"
    MaxMissed -1
</Plugin>

W celu generowania statystyk z wykorzystaniem rddtool , musimy załadować ten modów przy pomocy LoadPlugin i dodać poniższą zwrotkę do pliku /etc/collectd.conf :

LoadPlugin rrdtool

<Plugin rrdtool>
    DataDir "/tmp/router/stats"
    RRARows 720
    RRASingle false
    RRATimespan 1800
    RRATimespan 3600
    RRATimespan 43200
    RRATimespan 86400
    RRATimespan 172800
    RRATimespan 604800
</Plugin>

Opcja DataDir określa gdzie zapisywać zbierane dane. Kluczowe jest wyliczenie ile PDP (Primary Data Point) przypada na jeden CDP (Consolidated Data Point) i robi się to ze wzoru: liczba PDP = timespan/(stepsize * rrarows). W oparciu o powyższą konfigurację otrzymujemy: liczba CDP = 1800/(10*720), czyli 0.25. Podobnie z pozostałymi przedziałami czasu określonymi w opcjach RRATimespan : 0.5 , 6 , 12 oraz 84 . Chodzi o to by wyszły w miarę okrągłe liczby. Co one nam dają? Tam gdzie CDP jest mniejsze lub równe 1, to nie ma większego problemu z określeniem tego co zostanie pokazane na wykresie. Natomiast jeśli wartość ta jest większa od 1, np. dla przedziału czasu 2 dni, CDP przyjmuje wartość 12, wtedy rzecz jasna nie można umieścić wszystkich 12 wartości na wykresie. Zostanie wybrana tylko jedna z nich i możemy określić ją na podstawie parametrów MAX , MIN, AVERAGE albo też LAST . Jeśli określimy MAX , wtedy najwyższa z tych 12 wartości zostanie uwzględniona na grafie. Jeśli MIN, to najmniejsza, itd. O tym będzie nieco później. Jeśli chcemy korzystać z innych wartości niż uśrednione, to przestawiamy RRASingle na false .

Pozostałe moduły nie wymagają precyzowania konfiguracji. Wystarczy zatem załadować je w poniższy sposób:

LoadPlugin cpu
LoadPlugin load
LoadPlugin memory
LoadPlugin conntrack

Więcej informacji na temat wszystkich modułów oraz możliwych parametrów jakie idzie ustawić w pliku /etc/collectd.conf , można znaleźć pod tym linkiem. Zapisujemy plik i restartujemy demona collectd :

# /etc/init.d/collectd restart

Generowanie statystyk

Potrzebny nam będzie również skrypt, który wygeneruje odpowiednie wykresy. Poniżej nagłówek mojego skryptu:

#!/bin/sh

HOST="the-mountain"
DATADIR="/tmp/router/stats/"
IMAGEDIR="/tmp/router/www/stats/$HOST/"
PERIOD="1hour 12hours 24hours 48hours 1week"
PLUGINS="cpu-0 load memory ping conntrack"

for PLUGIN in $PLUGINS
do
    mkdir -p $IMAGEDIR/$PLUGIN
done
...

Powyżej mamy zdefiniowanych kilka zmiennych. Pliki statystyk ( DATADIR ) oraz obrazki z grafiami ( IMAGEDIR ) będą przechowywane w pamięci operacyjnej routera. Można oczywiście określić ścieżki do plików na routerze albo nawet na zewnętrznym nośniku. Z tym, że niesie to za sobą komplikacje. Po pierwsze, statystyki są zbierane w czasie rzeczywistym, zatem nośnik jest ciągle w fazie zapisu i przypadkowe odłączenie go zwykle doprowadza do utraty całej bazy danych. Nierzadko też potrafi się popsuć system plików. Druga kwestia dotyczy wydajności. Statystyki są generowane w pamięci RAM o wiele szybciej i obciążenie routera (loadavg) jest przy tym 2-3 razy mniejsze niż przy zapisie tych plików bezpośrednio na dysk. Jeśli dysponujemy routerem o pojemności pamięci RAM większej niż 64 MiB, to bez większego problemu możemy te obrazki zapisywać w pamięci operacyjnej. Trzeba tylko pilnować się z modułami i nie włączać wszystkich możliwych naraz.

W powyższym wycinku mamy także zmienną PERIOD . Określa ona przedziały czasu jakie będą uwzględniane przy generowaniu obrazków. W tym przypadku jest to 1 godzina, 12 godzin, 24 godziny, 48 godzin i 1 tydzień pracy urządzenia. Pliki ze statystykami nie rozrastają się i zajmują zawsze tyle samo miejsca. Niemniej jednak, im dłuższy przedział czasu, tym oczywiście baza zajmuje więcej miejsca. U mnie wszystkie potrzebne statystyki i wykresy zajmują łącznie 5-10 MiB. Także nie jest to znowu tak mało. Zmienna PLUGINS definiuje włączone moduły, na podstawie których są tworzone odpowiednie katalogi. Jakby nie patrzeć, statystyki będą przechowane w RAM i po zresetowaniu routera, szlag je trafi. Dlatego też za każdym razem jak router będzie się budził do życia, trzeba będzie tworzyć te katalogi na nowo.

W zależności od wybranych modułów, musimy umieścić w skrypcie sekcje podobne do tej poniżej. Dla przykładu określiłem tylko jedną sekcję ale cały skrypt jest na moim github'ie.

...
##############################
#        load section        #
##############################
load_load(){
    for TIME in $PERIOD
    do
        rrdtool graph $IMAGEDIR/load/load-$TIME.png --title "$HOST/load/load $TIME" --imgformat PNG --width 600 --height 100 --start now-$TIME --end now --interlaced \
        DEF:longterm_min=$DATADIR/$HOST/load/load.rrd:longterm:MIN \
        DEF:longterm_avg=$DATADIR/$HOST/load/load.rrd:longterm:AVERAGE \
        DEF:longterm_max=$DATADIR/$HOST/load/load.rrd:longterm:MAX \
        DEF:midterm_min=$DATADIR/$HOST/load/load.rrd:midterm:MIN \
        DEF:midterm_avg=$DATADIR/$HOST/load/load.rrd:midterm:AVERAGE \
        DEF:midterm_max=$DATADIR/$HOST/load/load.rrd:midterm:MAX \
        DEF:shortterm_min=$DATADIR/$HOST/load/load.rrd:shortterm:MIN \
        DEF:shortterm_avg=$DATADIR/$HOST/load/load.rrd:shortterm:AVERAGE \
        DEF:shortterm_max=$DATADIR/$HOST/load/load.rrd:shortterm:MAX \
        AREA:longterm_max#ffe8e8 \
        AREA:midterm_max#e8e8ff \
        AREA:shortterm_max#e2ffe2 \
        LINE2:shortterm_max#55ff55:1min \
        GPRINT:shortterm_avg:MIN:" Minimum\:%8.2lf %s" \
        GPRINT:shortterm_avg:AVERAGE:"Average\:%8.2lf %s" \
        GPRINT:shortterm_max:MAX:"Maximum\:%8.2lf %s\n" \
        LINE2:midterm_max#7777ff:5min \
        GPRINT:midterm_avg:MIN:" Minimum\:%8.2lf %s" \
        GPRINT:midterm_avg:AVERAGE:"Average\:%8.2lf %s" \
        GPRINT:midterm_max:MAX:"Maximum\:%8.2lf %s\n" \
        LINE2:longterm_max#ff7777:15min \
        GPRINT:longterm_avg:MIN:"Minimum\:%8.2lf %s" \
        GPRINT:longterm_avg:AVERAGE:"Average\:%8.2lf %s" \
        GPRINT:longterm_max:MAX:"Maximum\:%8.2lf %s\n" \
        >/dev/null 2>&1
    done
}
##############################
...

Generalnie rzecz biorąc, to co widzimy powyżej zawiera się w jednej linijce ale dla większej czytelności skorzystałem ze znaku łamania lini ( \ ). W ten sposób mogłem przenieść kolejne parametry rrdtool do osobnych linijek. Powyższy blok jest ujęty w funkcję określoną przez load_load(){} . W niej mamy pętle for , która to z kolei wygeneruje obrazki w oparciu o zdefiniowane wcześniej okresy czasu.

Pierwsza linijka (ta z rrdtool ) określa gdzie zapisać obrazek ( graph ) po wygenerowaniu grafu, tytuł grafu ( --title ), format w jakim obrazek zostanie zapisany ( --imgformat ), jego wymiary ( --width oraz --height ), czas uwzględniony na obrazku ( --start i --end now ), z tym, że now-$TIME oznacza czas cofnięcia się wstecz. Przykładowo, jeśli określimy now-1hour oznaczać to będzie jedną godzinę wcześniej w stosunku do godziny aktualnej. Opcja --interlaced ma zapewnić szybsze ładowanie się obrazków w przeglądarkach. Możemy także dopisać parametr --vertical-label="" w celu opisania pionowej osi wykresu.

Dalej mamy linijki z DEF . Przypisują one zmienne longterm_avg , longterm_max , etc. do danych zebranych przez collecd w odpowiednich plikach. By sprawdzić jakie dane możemy wyciągnąć z pliku, trzeba je odczytać przy pomocy poniższego polecenia:

# rrdtool info /tmp/router/stats/the-mountain/load/load.rrd
...
ds[shortterm]
...
ds[midterm]
...
ds[longterm]
...

Dane na wykresach są określane przez CF (Consolidation Function) i ten parametr również możemy odczytać z powyższego pliku:

...
rra[0].cf = "AVERAGE"
...
rra[1].cf = "MIN"
...
rra[2].cf = "MAX"
...

Kolejne linijki w powyższym bloku naszego skryptu to te zaczynające się od AREA . Rysują one słupki i oznaczają je odpowiednimi kolorami, tj. #ffe8e8 , #e8e8ff oraz #e2ffe2 (RGB, zapis hexalny). Wartości, w oparciu o które zostaną narysowane te wykresy, są brane ze zdefiniowanych przez nas zmiennych.

Z kolei wpisy zaczynające się od LINE, rysują wykres liniowy i odnoszą się także do legendy pod wykresem. Cyfra 2 zaraz po LINE oznacza grubość kreski, którą rysowany jest wykres. Dalej mamy określone zmienne, które mają być brane pod uwagę. Mamy także zdefiniowane kolory, odpowiednio #55ff55 , #7777ff i #ff7777 . Ostatnia wartość to opis, ten który pojawi się pod grafem.

Linijki zawierające GPRINT określają wartość maksymalną ( MAX ) , średnią ( AVERAGE ) i minimalną ( MIN ) dla danego wykresu. Te informacje również są umieszczane w legendzie.

Każdy moduł ma mniej więcej taki sam zapis. Różni się tylko ilością zdefiniowanych parametrów i po części też ich nazwami. Po skonfigurowaniu wszystkich modułów, na końcu skryptu wywołujemy odpowiednie funkcje. W tym przypadku jest póki co tylko jedna:

...
load_load

Tak stworzony skrypt zapisujemy sobie na routerze, np. w katalogu /etc/skrypty/mkgraph.sh .

Statystyki może i są zbierane w czasie rzeczywistym i to przez cały czas pracy routera ale obrazki z wykresami trzeba wygenerować ręcznie. Proces zbierania danych nie jest zbytnio zasobożerny. Natomiast generowanie wykresów już tak. Naturalnie, nie będziemy ciągle wbijać na router, by wygenerować sobie kilka grafów. No chyba, że będziemy potrzebować aktualnych danych. Zamiast tego, wykorzystamy narzędzie cron , by cyklicznie te wykresy generował za nas. Dobrze jest ustawić sobie interwał na 30 minut tak, by nie zarżnąć routera ciągłym generowaniem obrazków. Mając gotowy powyższy skrypt, musimy dodać wywołanie w tablicy cron . Wpisujemy zatem w terminalu crontab -e i dodajemy tam poniższy wpis:

# min   hour    day     mon     dow     command
*/30    *       *       *       *       /etc/skrypty/mkgraph.sh

Odczytywanie statystyk

Serwer www mamy już postawiony ale, by obejrzeć statystyki, musimy jeszcze stworzyć prostą stronę www. Możemy umieszczać wszystkie moduły w jednym pliku albo każdy moduł w osobnym. Możemy też wybrać odpowiednie obrazki i stworzyć sobie jedną stronę tak, by wszystkie potrzebne nam wykresy były w jednym miejscu. Szkielet stron zapisujemy w katalogu /etc/collectd_www/ . Poniżej jest przykładowy plik load.html :

<html>
    <body>
        <img src="/stats/the-mountain/load/load-1hour.png">
        <img src="/stats/the-mountain/load/load-12hours.png">
        <img src="/stats/the-mountain/load/load-24hours.png">
        <img src="/stats/the-mountain/load/load-48hours.png">
        <img src="/stats/the-mountain/load/load-1week.png">
    </body>
</html>

By to wszystko teraz puścić w ruch, potrzebny nam jest jeszcze mały skrypt startowy, który przekopiuje odpowiednie pliki w odpowiednie miejsce podczas fazy boot routera. Tworzymy zatem plik /etc/init.d/statistics o poniższej zawartości:

#!/bin/sh /etc/rc.common

START=81

start() {
    if [ ! -d /tmp/router/www ]
    then
        mkdir -p /tmp/router/www
    fi

    cp -a /etc/collectd_www/* /tmp/router/www/
    /etc/skrypty/mkgraph.sh
}

Dodajemy go do autostartu i odpalamy skrypt:

# /etc/init.d/statistics enable
# /etc/init.d/statistics start

Trzeba jednak pamiętać, że generowanie statystyk zajmuje czas i trochę spowolni start routera. Dlatego też zawsze możemy wykomentować odpalanie skryptu mkgraph.sh . Odpalamy teraz przeglądarkę internetową i przechodzimy na adres http://192.168.2.1/load.html . Jeśli wszystkie kroki przeprowadziliśmy zgodnie z powyższym opisem, to naszym oczom powinny się ukazać wykresy podobne do tych poniżej:

Moduł ping:

statystyki-ruch-openwrt-router-collectd-rrdtool

Moduł load:

statystyki-ruch-openwrt-router-collectd-rrdtool

Mikhail Morfikov avatar
Mikhail Morfikov
Po ponad 10 latach spędzonych z różnej maści linux'ami (Debian/Ubuntu, OpenWRT, Android) mogę śmiało powiedzieć, że nie ma rzeczy niemożliwych i problemów, których nie da się rozwiązać. Jedną umiejętność, którą ludzki umysł musi posiąść, by wybrnąć nawet z tej najbardziej nieprzyjemniej sytuacji, to zdolność logicznego rozumowania.