пятница, 29 мая 2015 г.

Простейший счётчик трафика на iptables.

Какое-то время назад хотел описать работу несложного способа подсчёта интернет-трафика, который использую сам. Но все было лень этим заняться. Тут глянул - уже 4 года прошло, а заметка все это время в черновиках находилась, хотя актуальности не потеряла. Так что если имеется система на GNU Linux, и используется ее фаервол iptables на базе Netfilter, то ничто не мешает задействовать этот несложный счетчик трафика.

Хотя бывают разные высокоуровневые системы подсчета, здесь учитывается сетевой уровень стека TCP/IP, поэтому появляется возможность достаточно точно узнать, какое количество данных прошло через сетевой интерфейс. Например, нам нужно узнавать потребленный интернет-трафик в конце каждого месяца и получать эти данные электронным письмом. Будем также иметь ввиду, что есть два канала в интернет (для отказоустойчивости) и, соответственно, - два сетевых интерфейса.

Итак, создаем новую цепочку правил с помощью iptables:

iptables -N traff_count

Да, в новой цепочке нет правил, они просто не нужны.

Мы хотим направить все входящие и выходящие пакеты обоих интернет - интерфейсов в только что созданную пустую цепочку правил. Для чего в трех имеющихся цепочках INPUT, OUTPUT, FORWARD вставляем в самое начало следующие правила:

iptables -I INPUT 1 -i net1 -j traff_count

iptables -I INPUT 2 -i net2 -j traff_count


iptables -I FORWARD 1 -o net1 -j traff_count

iptables -I FORWARD 2 -o net2 -j traff_count

iptables -I FORWARD 3 -i net1 -j traff_count

iptables -I FORWARD 4 -i net2 -j traff_count


iptables -I OUTPUT 1 -o net1 -j traff_count

iptables -I OUTPUT 2 -o net2 -j traff_count

Вот и всё, делов то! Принцип следующий: пакет данных поступает на сетевой интерфейс, как только попадает в любую цепочку правил из INPUT, OUTPUT, FORWARD и проходит проверку на соответствие интерфейсу, он перенаправляется в пустую traff_count, и сразу же возвращается обратно. Дальше проходит все остальные проверки - нам это уже здесь не интересно, главное - что перенаправленный в traff_count пакет увеличил счетчик правила с "-j traff_count". Теперь можно посмотреть результаты, например, оцениваем входящий трафик по одному из интерфейсов:

#iptables -L INPUT 1 -nv

51M 20G traff_count all -- net1 * 0.0.0.0/0 0.0.0.0/0

Здесь мы видим, что прокачано 20 Гигабайт данных. Теперь неплохо было бы этот счетчик обнулять раз в месяц, а результаты присылать по почте. Кроме того надо совершить некоторую обработку данных. Для чего пишем небольшой скрипт traf_count.sh на bash:

#!/bin/bash

#The script counts consumed internet traffic monthly.

rounding (){

number=$1

measure=""

if [ "$number" -gt 1073741824 ] ;then

traf=$(echo "scale=3;$number/1073741824"|bc -l)

measure='gb'

elif [ "$number" -gt 1048576 ] ;then

traf=$(echo "scale=3;$number/1048576"|bc -l)

measure='mb'

elif [ "$number" -gt 1024 ] ;then

traf=$(echo "scale=3;$number/1024"|bc -l)

measure='kb'

else

measure='b'

fi

result="$traf $measure"

}

IN_FW_NET1=$(iptables -L FORWARD 3 -n -v -x|awk '{if ($6=="net1" && $3=="traff_count"){ print $2}}')

OUT_FW_NET1=$(iptables -L FORWARD 1 -n -v -x|awk '{if ($7=="net1" && $3=="traff_count"){ print $2}}')

IN_FW_NET2=$(iptables -L FORWARD 4 -n -v -x|awk '{if ($6=="net2" && $3=="traff_count"){ print $2}}')

OUT_FW_NET2=$(iptables -L FORWARD 2 -n -v -x|awk '{if ($7=="net2" && $3=="traff_count"){ print $2}}')


IN_NET1=$(iptables -L INPUT 1 -n -v -x|awk '{if ($6=="net1" && $3=="traff_count"){ print $2}}')

IN_NET2=$(iptables -L INPUT 2 -n -v -x|awk '{if ($6=="net2" && $3=="traff_count"){ print $2}}')

OUT_NET1=$(iptables -L OUTPUT 1 -n -v -x|awk '{if ($7=="net1" && $3=="traff_count"){ print $2}}')

OUT_NET2=$(iptables -L OUTPUT 2 -n -v -x|awk '{if ($7=="net2" && $3=="traff_count"){ print $2}}')

#All the variables shouldn't have zero length

if [ -z "$IN_FW_NET1" ] || [ -z "$OUT_FW_NET1" ] || [ -z "$IN_FW_NET2" ] || [ -z "$OUT_FW_NET2" ] || [ -z "$IN_NET1" ] || [ -z "$IN_NET2" ] || [ -z "$OUT_NET1" ] || [ -z "$OUT_NET2" ] ; then

echo "Something wrong in the script, check it!"

exit

fi

message="Consumed traffic for last month:\n\tProvider 1.\n\tIN:\t"

rounding "$(($IN_FW_NET1 + $IN_NET1))"

message="$message$result\n\tOUT:\t"

rounding "$(($OUT_FW_NET1 + $OUT_NET1))"

message="$message$result\n\tProvider 2.\n\tIN:\t"

rounding "$(($IN_FW_NET2 + $IN_NET2))"

message="$message$result\n\tOUT:\t"

rounding "$(($OUT_FW_NET2 + $OUT_NET2))"

message="$message$result\n"

echo -e $message|mail -e -s "Consumed traffic for last month" admin@yourdomain.com

#and the last step - zeroing counters

for i in $(iptables -n -L INPUT --line-numbers|grep traff_count|grep -v grep|cut -c 1);do [ -n "$i" ] && iptables -Z INPUT $i; done

for i in $(iptables -n -L OUTPUT --line-numbers|grep traff_count|grep -v grep|cut -c 1);do [ -n "$i" ] && iptables -Z OUTPUT $i; done

for i in $(iptables -n -L FORWARD --line-numbers|grep traff_count|grep -v grep|cut -c 1);do [ -n "$i" ] && iptables -Z FORWARD $i; done

Некоторые пояснения к скрипту... Iptables своеобразно округляет числа, например, считает, что 1 килобайт - это 1000 байт. Мне это не понравилось. Поэтому я получаю данные в байтах с помощью опции '-x' утилиты, а для округления чисел (до Kb, Mb, Gb) создал функцию rounding. Iptables не умеет показывать данные в терабайтах, и я тоже не стал это делать, да и не требовалось ни разу, по факту. Подсчет трафика осуществляется очень просто. Входящий включает то, что поступило непосредственно на данный хост, и то, что перенаправлено из интернета в офис (через цепочку FORWARD). Исходящий, наоборот: то, что вышло непосредственно с данный хоста через данный интерфейс, и то, что перенаправлено из офиса в интернет.

Дальше проверяется, что все переменные имеют ненулевую длину, иначе завершаем скрипт. Потом формируем сообщение админу (то есть себе) со всей нужной информацией, дополняя его табуляцией и переносом строк. Наконец, очищаем счетчики, чтоб через месяц получить новые данные.

Осталось записать задание в cron:

@monthly root /path/traf_count.sh

У меня на сервере больше сетевых интерфейсов, чем здесь описано, кроме того немного сложнее сделан анализ потребленного трафика разными внутренними подсетями. Здесь я старался описать принцип подсчета, возможно, скрипт надо немного поправить для учета ваших требований.

Комментариев нет:

Отправить комментарий