Какое-то время назад хотел описать работу несложного способа подсчёта интернет-трафика, который использую сам. Но все было лень этим заняться. Тут глянул - уже 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
У меня на сервере больше сетевых интерфейсов, чем здесь описано, кроме того немного сложнее сделан анализ потребленного трафика разными внутренними подсетями. Здесь я старался описать принцип подсчета, возможно, скрипт надо немного поправить для учета ваших требований.
Комментариев нет:
Отправить комментарий