Понадобилось мне как-то реализовать подсчет трафика на шлюзе, с установленной на нем ОС FreeBSD 7.3. Для подсчета трафика я решил использовать модуль ng_netflow подсистемы netgraph. Статистика будет складываться в БД и через веб морду просматриваться. В качестве СУБД буду использовать PostgreSQL, но ничто не мешает использовать другую СУБД. В качестве коллектора и анализатора статистики я использовал пакет flow-tools.
Для начала нужно создать соответствующие узлы в netgraph. Для простоты развертывания на других машинах был написан следующий скрипт:- #!/bin/sh
- # PROVIDE: ngnetflow
- # REQUIRE: netif
- # KEYWORD: nojail shutdown
- . /etc/rc.subr
- name="ngnetflow"
- rcvar=`set_rcvar`
- start_cmd="${name}_start"
- stop_cmd=":"
- load_rc_config $name
- eval "${rcvar}=\${${rcvar}:-'NO'}"
- ngnetflow_ifaces=${ngnetflow_ifaces:-""}
- ngnetflow_export=${ngnetflow_export:-""}
- ngnetflow_start()
- {
- if [ -z "${ngnetflow_ifaces}" ]; then
- echo "ngnetflow_ifaces is not set"
- return 1
- fi
- if [ -z "${ngnetflow_export}" ]; then
- echo "ngnetflow_export is not set"
- return 1
- fi
- count_if="0"
- for iface in ${ngnetflow_ifaces}; do
- ngctl mkpeer ${iface}: tee lower left
- ngctl name ${iface}:lower ${iface}_tee
- ngctl connect ${iface}: ${iface}_tee: upper right
- ngctl mkpeer ${iface}_tee: one2many left2right many0
- ngctl name ${iface}_tee:left2right ${iface}_tee_o2m
- ngctl connect ${iface}_tee: ${iface}_tee_o2m: right2left many1
- if [ "${count_if}" -eq "0" ]; then
- ngctl mkpeer ${iface}_tee_o2m: netflow one iface0
- ngctl name ${iface}_tee_o2m:one netflow
- else
- ngctl connect ${iface}_tee_o2m: netflow: one iface${count_if}
- fi
- count_if=`echo ${count_if} | awk '{ $0++; print $0; }'`
- done
- ngctl mkpeer netflow: ksocket export inet/dgram/udp
- ngctl name netflow:export nf_ksocket
- ngctl msg nf_ksocket: connect inet/${ngnetflow_export}
- echo "Netflow system started..."
- return 0
- }
- run_rc_command "$1"
- ngnetflow_enable="YES" # разрешаем запуск скрипта при старте системы
- ngnetflow_export="127.0.0.1:4444" # экспорт статистики на указанный здесь адрес
- ngnetflow_ifaces="if0 if1" # интерфейсы, на которых нужно считать трафик
- # cd /usr/ports/net-mgmt/flow-tools
- # make install clean
- flow_capture_enable="YES"
- flow_capture_port="4444"
- flow_capture_flags="-E 16M"
- # /usr/local/etc/rc.d/flow_capture start
- # sockstat -4 -l | grep flow
- flowtools flow-captu 1008 1 udp4 *:4444 *:*
- # /usr/local/etc/rc.d/ngnetflow start
- #!/bin/sh
- # Скрипт экспортирует статистику за день в БД
- REP_Y=`date -v-1d "+%Y"` # report year
- REP_M=`date -v-1d "+%m"` # report month
- REP_D=`date -v-1d "+%d"` # report day
- WORKDIR="/root/netflow"
- TMPDIR="/tmp"
- FLOWS_DIR="/var/db/flows"
- EXP_FORMAT="DPKTS,DOCTETS,SRCADDR,DSTADDR,NEXTHOP,INPUT,OUTPUT,SRCPORT,DSTPORT,PROT,TOS,TCP_FLAGS,SRC_MASK,DST_MASK"
- DB_HOST="192.168.7.253"
- DB_PORT="5432"
- DB_USER="netflow"
- DB_PASS="1234"
- DB_NAME="netflow"
- DB_TABLE="${REP_Y}-${REP_M}"
- DB_COLUMNS="\
- \"dpkts\",\
- \"doctets\",\
- \"srcaddr\",\
- \"dstaddr\",\
- \"nexthop\",\
- \"input\",\
- \"output\",\
- \"srcport\",\
- \"dstport\",\
- \"proto\",\
- \"tos\",\
- \"tcp_flags\""
- EXP_FILE="${TMPDIR}/netflow.csv"
- SQL_FILE="${TMPDIR}/netflow.sql"
- LOG_FILE="${WORKDIR}/nf_daily.log"
- flow-cat "${FLOWS_DIR}/${REP_Y}/${REP_Y}-${REP_M}/${REP_Y}-${REP_M}-${REP_D}" | \
- flow-export -f2 -m ${EXP_FORMAT} 2> /dev/null | \
- egrep -v "^#" > ${EXP_FILE}
- if [ ! -f ${EXP_FILE} ]; then
- echo "Export file not found"
- exit 1
- fi
- if [ -f ${SQL_FILE} ]; then
- rm -f ${SQL_FILE}
- fi
- for line in `cat ${EXP_FILE}`; do
- data=`echo ${line} | awk '{ split($0, str, ",");
- printf("%u, %u, $$%s$$, $$%s$$, $$%s$$, %d, %d, %d, %d, %d, %d, %d",
- str[1], str[2], str[3], str[4],
- str[5], str[6], str[7], str[8],
- str[9], str[10], str[11], str[12]);
- }'`
- echo "INSERT INTO \"${DB_TABLE}\"(${DB_COLUMNS}) VALUES (${data});" >> $SQL_FILE
- done
- psql -q -d ${DB_NAME} -h ${DB_HOST} -p ${DB_PORT} -U ${DB_USER} -f $SQL_FILE
- rm -f ${EXP_FILE}
- rm -f ${SQL_FILE}
- exit 0
- #!/bin/sh
- DB_HOST="192.168.7.253"
- DB_PORT="5432"
- DB_USER="netflow"
- DB_PASS="1234"
- DB_NAME="netflow"
- DB_TABLE=`date "+%Y-%m"`
- SQL_QUERY="CREATE TABLE \"${DB_TABLE}\" (\
- \"id\" bigserial NOT NULL UNIQUE PRIMARY KEY,\
- \"dpkts\" bigint,\
- \"doctets\" bigint,\
- \"srcaddr\" cidr,\
- \"dstaddr\" cidr,\
- \"nexthop\" cidr,\
- \"input\" smallint,\
- \"output\" smallint,\
- \"srcport\" int,\
- \"dstport\" int,\
- \"proto\" smallint,\
- \"tos\" smallint,\
- \"tcp_flags\" smallint\
- );"
- psql -q -d ${DB_NAME} -h ${DB_HOST} -p ${DB_PORT} -U ${DB_USER} -c "${SQL_QUERY}" > /dev/null 2>&1
- exit 0
- 0 1 1 * * /root/netflow/nf_month.sh
- 30 0 * * * /root/netflow/nf_daily.sh
- #!/usr/bin/perl
- use strict;
- use warnings;
- my $DB_HOST = 'localhost';
- my $DB_PORT = '5432';
- my $DB_NAME = 'netflow';
- my $DB_USER = 'netflow';
- my $DB_PASS = '1234';
- my $stat_month = '';
- my $query_menu = '
- SELECT
- "tablename"
- FROM "pg_tables"
- WHERE "tableowner" = $$#owner#$$;';
- my $query_content = '
- SELECT
- SUM("dpkts") AS "packets",
- (SUM("doctets")/1048576)::bigint AS "size",
- host("dstaddr") FROM "#tbl_name#"
- WHERE "dstaddr" << $$192.168.7.0/24$$
- GROUP BY "dstaddr"
- ORDER BY "dstaddr";';
- if ($ENV{'REQUEST_METHOD'} && ($ENV{'REQUEST_METHOD'} eq 'GET')){
- my $param = $ENV{'QUERY_STRING'};
- if ($str[0] eq 'month'){
- $stat_month = $str[1];
- $stat_month =~ s/'|"|\$|\(|\)//;
- last;
- }
- }
- }
- $query_menu =~ s/#owner#/$DB_USER/;
- system("psql -h $DB_HOST -p $DB_PORT -d $DB_NAME -U $DB_USER -t -c '$query_menu' -o /tmp/db_menu.txt");
- $query_content =~ s/#tbl_name#/$stat_month/;
- system("psql -h $DB_HOST -p $DB_PORT -d $DB_NAME -U $DB_USER -t -c '$query_content' -o /tmp/db_table.txt");
- }
- '<head>',
- '<title>Statistics</title>',
- '<style type="text/css">',
- 'table { border-collapse: collapse; }',
- '.header { background-color: lightblue; }',
- '.content { background-color: orange; }',
- '</style>',
- '</head>',
- '<body>'
- );
- sub get_menu(){
- my $text = '';
- return '<h3>Empty!!!</h3>'
- }
- '<tr><td align="center" class="header">Menu</td></tr>'
- );
- while (<FIN>){
- s/\s|\t//g;
- $_,
- '">',
- $_,
- '</a></td></tr>'
- );
- }
- $text .= '</table>';
- }
- sub get_content(){
- my $text = '';
- }
- '<tr><td class="header">packets</td>',
- '<td class="header">megabytes</td>',
- '<td class="header">IP</td></tr>'
- );
- while (<FIN>){
- $text .= '<tr>';
- s/\s|\t//g;
- }
- $text .= '</tr>';
- }
- $text .= '</table>';
- }
- '<tr><td align="center" valign="top" width="15%">',
- get_menu(),
- '</td>'
- );
- get_content(),
- '</td></tr></table>',
- '</body></html>'
- );
Прикрепленные файлы
Обсуждение статьи