Построение сети с разделением трафика на базе iptables & ip route

Вот как-то встала такая задачка: существуют 2 входящих канала инета, один быстрый, но дорогой (0.06 за Мб), другой медленный, но с большим плюсом в плане стоимости — рунет бесплатно. Соответственно, созрела мысль тащить один трафик, иностранный, по быстрому каналу, а российский по другому. На лицо выгодность — экономия на российском трафике. Так вот, исходя из этих данных, встала задача разделения и учета потребленных ресурсов, решение коей я и собираюсь тут рассказать. Естественно, это решение не претендует на какую-то оригинальность и новизну, но я думаю, что некоторым покажется интересным.
Оговорюсь сразу, что разделение происходит только для соединений, инициированных из внутренней сети, так как осуществить редирект внешнего клиента (посетителя сайта) не входило в задачу да и не является столь актуальным, поскольку основной вид трафика, потребляемого внешним клиентом, это исходящий; мы же делим входящий %)).
Ну, начнем по порядку...
Первое, что необходимо сделать — это определиться со списком сетей, которые мы хотим отделить от основного (Default) канала, в нашем случае это — рунет.
Список подсетей и хостов рунета можно взять здесь: http://relcom.duty.ru/dbdump.txt.gz. Соответственно, если делить по другим сетям, то нужно составить список этих сетей.
Далее составляем таблицу роутинга для подсетей или машин (зависит от задачи). Необходимо составить следующие списки:

  • Runet.db - список сетей;
  • Ruhost.db - список хостов (можно использовать общий список хостов и сетей, это уж как кому нравится.);
  • Users - список машин для которых производится деление трафика.

Приведу пример:

#!/bin/bash

RU_IP="195.182.16.3"
RU_DEV="eth2"
Default_IP="63.18.23.10"
Default_dev="eth1"
RUNET="/bin/cat /etc/routing/runet.db|grep -v 192.168.|grep -v
127.0.0|grep -v 10.0.0|grep -v 172.0.0"
RUHOST="/bin/cat /etc/routing/ruhost.db|grep -v 192.168.|grep -v
127.0.0|grep -v 10.0.0|grep -v 172.0.0"
USR="/bin/cat /etc/routing/users"

for US in $USR
{
/sbin/ip rule add from $US table runet
}
for RU in $RUNET
{
/sbin/ip route add $RU via $RU_IP dev $RU_DEV table runet
}
for RU in $RUHOST
{
/sbin/ip route add $RU via $RU_IP dev $RU_DEV table runet
}
/sbin/ip route add default via $Default_IP dev $Default_dev table runet

После данных махинаций мы получим то, что до заданных сетей и хостов машины будут ходить по разным маршрутам. Проверить это можно с помощью traceroute (unix) или tracert (win). Далее нам необходимо сконфигурировать шлюз для отсечения ненужного и учета входящего трафика с разных направлений. В моем случае использовался NAT, то есть внутренняя сеть проходит через трансляцию адресов, в том числе и серверы, что и нужно отразить в скрипте шлюза.
Скрипт, который у меня получился, изображен на врезке, его также можно скачать здесь: http://www.uinc.ru/scripts/load.cgi?articles/37/rc.firewall.txt. Естественно там можно много чего подправить и оптимизировать, но это уже ваше дело ;)
Попробую объяснить, что происходит в данном скрипте ;-) .
Таблицы in_lan & in_inet отвечают за фильтрацию пакетов, предназначенных непосредственно шлюзу соответственно с локальной сети и Интернета. Для разделения входящего трафика используется маркировка пакетов и раскидывание по разным таблицам ru_in & ip_net_in. В каждом из этих фильтров включена строка для всех серверов и рабочих станций, в счетчике которой и будет происходить накопление трафика.
Статистику по входящему трафику получаем с помощью:

iptables -L "имя таблицы" –vnx

Результат должен быть приблизительно такой:

Chain ip_net_in (1 references)

pkts bytes target prot opt in out source destination

6061 426394 ACCEPT all -- * * 0.0.0.0/0 192.168.0.1
1598 135371 ACCEPT all -- * * 0.0.0.0/0 192.168.0.2
0 0 ACCEPT all -- * * 0.0.0.0/0 192.168.0.11
0 0 ACCEPT all -- * * 0.0.0.0/0 192.168.0.12
0 0 ACCEPT all -- * * 0.0.0.0/0 192.168.0.13
0 0 ACCEPT all -- * * 0.0.0.0/0 192.168.0.22
0 0 ACCEPT all -- * * 0.0.0.0/0 192.168.0.23
0 0 ACCEPT all -- * * 0.0.0.0/0 192.168.0.20
325 17421 DROP all -- * * 0.0.0.0/0 0.0.0.0/0

#!/bin/bash
#######################################################################
# Firewall script by Flame[UinC] flame@uinc.ru #
#######################################################################
IPT=/sbin/iptables
USERS=`/bin/cat /etc/routing/users`
SERVERS=`/bin/cat /etc/routing/servers`
IP_NET_DEV='eth0'
RU_NET_DEV='eth2'
LAN_DEV='eth1'
IP_NET='195.182.16.0/27'
RU_NET='195.182.16.0/26'
LAN='192.168.0.0/24'
IP_NET_IP='195.182.16.3'
RU_NET_IP='195.182.16.3'
LAN_IP='192.168.0.2'


#Flushing rules
$IPT -F
$IPT -X
$IPT -t nat -F
$IPT -t nat -X
# INPUT RULS
$IPT -N in_lan
$IPT -N in_inet

#INPUT FROM LAN
$IPT -A in_lan -s $LAN -d $LAN_IP -p tcp --dport http -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p udp --dport http -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p tcp --dport domain -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p udp --dport domain -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p tcp --dport ntp -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p udp --dport ntp -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p tcp --dport https -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p udp --dport https -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p icmp -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p tcp --dport postgres -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p udp --dport postgres -j ACCEPT
$IPT -A in_lan -i eth1 -s $IP_NET -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p tcp --dport mysql -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p udp --dport mysql -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p tcp --dport ssh -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -p udp --dport ssh -j ACCEPT
$IPT -A in_lan -s $LAN -d $LAN_IP -m state --state RELATED,ESTABLISHED -j ACCEPT
$IPT -A in_lan -j REJECT --reject-with icmp-net-unreachable
$IPT -A in_lan -j DROP

#INPUT FROM INET
$IPT -A in_inet -s $LAN -d $LAN_IP -j DROP
$IPT -A in_inet -s ! $LAN -p tcp --dport ssh -j ACCEPT
$IPT -A in_inet -s ! $LAN -p udp --dport ssh -j ACCEPT
$IPT -A in_inet -s ! $LAN -m state --state RELATED,ESTABLISHED -j ACCEPT
$IPT -A in_inet -j REJECT --reject-with icmp-net-unreachable
$IPT -A in_inet -j DROP

echo “ok stage 1”

#SET INPUT FILTRS
$IPT -A INPUT -i $LAN_DEV -j in_lan
$IPT -A INPUT -i $IP_NET_DEV -j in_inet
$IPT -A INPUT -i $RU_NET_DEV -j in_inet

#SET RULES FOR TRAFFIC COUNTS
$IPT -N ip_net_in
$IPT -N ru_in
for USR in $USERS
{
$IPT -A ip_net_in -d $USR -j ACCEPT
$IPT -A ru_in -d $USR -j ACCEPT
}
for USR in $SERVERS
{
$IPT -A ip_net_in -d $USR -j ACCEPT
$IPT -A ru_in -d $USR -j ACCEPT
}


$IPT -A ip_net_in -j DROP
$IPT -A ru_in -j DROP

echo "ok stage 2"

# SET RULES FOR MARKING IN/OUT TRAFFIC
$IPT -t mangle -A FORWARD -i eth2 -j MARK --set-mark 2
$IPT -t mangle -A FORWARD -i eth0 -j MARK --set-mark 1


#RULES FOR SEPARATE IN/OUT TRAFFIC
$IPT -N all
$IPT -A all -m limit --limit 4/second --limit-burst 4 -j LOG --log-level DEBUG --log-prefix "IPT INPUT packet died: "
$IPT -A all -p tcp ! --syn -m state --state NEW -j LOG --log-prefix "New not syn:"
$IPT -A all -p tcp ! --syn -m state --state NEW -j DROP
$IPT -A all -m mark --mark 2 -j ru_in
$IPT -A all -m mark --mark 1 -j ip_net_in
$IPT -A all -d 192.168.0.0/16 -j REJECT --reject-with icmp-net-unreachable
$IPT -A all -d 10.0.0.0/8 -j REJECT --reject-with icmp-net-unreachable

#FORWARD RULES
$IPT -P FORWARD ACCEPT
$IPT -A FORWARD -s localhost -j ACCEPT
$IPT -A FORWARD -d localhost -j ACCEPT
$IPT -A FORWARD -j all

# NAT RULES
$IPT -t nat -N ipnet_out
$IPT -t nat -N ru_out
$IPT -t nat -N ipnet_in
$IPT -t nat -N ru_in

#SNAT RUL'S
#SERVERS IN IPNET
$IPT -t nat -A ipnet_out -s 192.168.0.1 -o $IP_NET_DEV -j SNAT --to-source server1IP
$IPT -t nat -A ipnet_out -s 192.168.0.2 -o $IP_NET_DEV -j SNAT --to-source server2IP

#WKS IN IPNET
for USR in $USERS
{
$IPT -t nat -A ipnet_out -s $USR -o $IP_NET_DEV -j MASQUERADE
}

echo "ok stage 3"

#SERVERS IN RU
$IPT -t nat -A ru_out -s 192.168.0.1 -o $RU_NET_DEV -j SNAT --to-source server1IP2
$IPT -t nat -A ru_out -s 192.168.0.2 -o $RU_NET_DEV -j SNAT --to-source server2IP2

#WKS IN RU

for USR in $USERS
{
$IPT -t nat -A ru_out -s $USR -o $RU_NET_DEV -j MASQUERADE
}



#DNAT RUL'S
#SERVERS IN IPNET
$IPT -t nat -A ipnet_in -i $IP_NET_DEV -d server1IP -p tcp -m multiport --dport 20,21,53,80 -j DNAT --to-destination 192.168.0.1
$IPT -t nat -A ipnet_in -i $IP_NET_DEV -d server1IP -p udp -m multiport --dport 20,21,53,80 -j DNAT --to-destination 192.168.0.1
$IPT -t nat -A ipnet_in -i $IP_NET_DEV -d server2IP -p tcp -m multiport --dport 25,110,143 -j DNAT --to-destination 192.168.0.2
$IPT -t nat -A ipnet_in -i $IP_NET_DEV -d server2IP -p tcp --dport 400: -j DNAT --to-destination 192.168.0.2

#SERVERS IN RU
$IPT -t nat -A ru_in -i $RU_NET_DEV -d server1IP2 -p tcp -m multiport --dport 20,21,53,80 -j DNAT --to-destination 192.168.0.1
$IPT -t nat -A ru_in -i $RU_NET_DEV -d server1IP2 -p tcp -m multiport --dport 25,110,143,3000,3001 -j DNAT --to-destination 192.168.0.2
echo "ok stage 4"


#DO NAT RUL'S
$IPT -t nat -A POSTROUTING -o $RU_NET_DEV -j ru_out
$IPT -t nat -A POSTROUTING -o $IP_NET_DEV -j ipnet_out

$IPT -t nat -A PREROUTING -i $RU_NET_DEV -j ru_in
$IPT -t nat -A PREROUTING -i $IP_NET_DEV -j ipnet_in
echo "ok"

Вот, собственно, мы и получили статистику ;-), что с ней делать дальше решать вам, могу лишь подсказать пример скрипта, разбирающего и пишущего ее в postgreSQL. Скачать этот скрипт можно здесь: http://www.uinc.ru/scripts/load.cgi?articles/37/db_add.txt.
Вот и сказочке конец а кто слушал, тот... ;-)

Акулич Дмитрий АКА Флам, uinC Member.



Сетевые решения. Статья была опубликована в номере 01 за 2003 год в рубрике sysadmin

©1999-2024 Сетевые решения