Linux маршрутизатор с несколькими внешними интерфейсами

Настройка линукс маршрутизатора через iptables + iproute2 на два (и больше) внешних интерфейса.

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

Имеется:
- две входящие сетевые соски от двух разных провайдеров.
- локальная сеть
- три сетевых интерфейса на маршрутизаторе.

Задача:
Объеденить всех пользователей в одну локальную сеть и пускать одних
пользователей в интернет через одного провайдера, других - через
другого.

В рассматриваемом примере, все пользователи в локальной сети будут
находиться в сети 172.16.0.0/16, при этом пользователей из подсети
172.16.0.0/24 надо пропускать в интернет через первого провайдера, а
пользователей 172.16.1.0/24 - через второго.

Будем использовать iproute2 для таблиц маршуризации и iptables для ната.

Обозначим:
$IP_LOCAL="172.16.0.1" - адрес нашего маршрутизатора в локальной сети.
$IP_INET1="1.1.1.2" - адрес нашего маршрутизатора в сети первого провайдера.
$IP_INET2="2.2.2.2" - адрес нашего марщрутизатора в сети второго провайдера.

$IF_LOCAL="eth0" - имя интерфейса на локальную сеть
$IF_INET1="eth1" - имя интерфейса на первого провайдера.
$IF_INET2="eth2" - имя интерфейса на второго провайдера.

$NET_LOCAL="172.16.0.0/16" - локальная сеть.
$NET_INET1="1.1.1.0/24" - адрес сети в которой гейт нашего первого провайдера.
$NET_INET2="2.2.2.0/24" - адрес сети в которой гейт нашего второго провайдера.
$NET_SUB1="172.16.0.0/24" - подсеть пользователей на первого провайдера
$NET_SUB2="172.16.1.0/24" - подсеть пользователей на второго провайдера

$GW1="1.1.1.1" - гейт первого провайдера.
$GW2="2.2.2.1" - гейт второго провайдера.

Настраиваем статические адреса на сетевые карты.

Debian (ubuntu) хранит настройки в файле
/etc/network/interfaces.

пишем:
auto lo
iface lo inet loopback

#local
auto eth0
iface eth0 inet static
address 172.16.0.1
netmask 255.255.0.0
network 172.15.0.0
broadcast 172.16.255.255

#inet 1
auto eth1
iface eth1 inet static
address 1.1.1.2
netmask 255.255.255.0
gateway 1.1.1.1
network 1.1.1.0
broadcast 1.1.1.255

#inet 2
auto eth2
iface eth2 inet static
#если надо - меняем мак адреса
# pre-up ifconfig eth2 hw ether aa:bb:cc:cc:bb:aa
address 2.2.2.2
netmask 2.2.2.0
# себе я второй гейт не прописываю, в следствии чего сам маршрутизатор ходит в инет через первый инет
# gateway 2.2.2.1
network 2.2.2.0
broadcast 2.2.2.255

тем самым мы сказали, что все сетвые у нас имеют статические адреса, и
указали их.

Настаиваем маршрутизацию с утилитой ip.

Утилита route позволяет нам обратиться к таблице маршртизации.
Сила утилиты ip в том, что она позволяет создать большое количество
таблиц для самых разных потребностей, а так же, способна считывать
флаги, которые расставляет файрвол iptables (об этом ниже) и на их
основе тоже делать какие-то действия.

Основа маршрутизации через разные таблицы - правила (rule)
А сила различных таблиц в том, что мы можем хранить огромное количество
различных таблиц маршрутизации, через которые и будем пропускать пакеты.

Просмотреть список уже имеющихся таблиц можно командой
ip rule list

0: from all lookup 255
32766: from all lookup main
32767: from all lookup default

Мы видим, что у нас есть три правила: таблица 255, main и default.
То, что выдает нам route - хранится в таблице main, что можно увидеть командой

ip route list table main

В качестве номеров таблиц можно использовать цифры, ( пример: 0: from
all lookup 255 ), а можно прописать на эти цифры какие-то более понятные
простым смертным имена ( пример: 32766: from all lookup main ).

Так что обозначим свои таблицы:
Редактируем файл

/etc/iproute2/rt_tables

и добавляем туда записи вида:

#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
#1 inr.ruhep

10 T1
20 T2

номер - это то самое число, которое мы можем использовать если не
именовать таблицы, а строковое значение - то, что мы в действительности
будем использовать. (кстати, тут же мы видим и уже встречавшиеся нам в
списке правил таблицы)

Теперь создаем некий скрипт, который в последствии будет все нам
прописывать при старте системы.

Понятное дело, что каждый использует свой любимый редактор =)
/etc/init.d/route


#!/bin/sh

IP_LOCAL="172.16.0.1" # адрес нашего маршрутизатора в локальной сети.
IP_INET1="1.1.1.2" # адрес нашего маршрутизатора в сети первого провайдера.
IP_INET2="2.2.2.2" # адрес нашего марщрутизатора в сети второго провайдера.

IF_LOCAL="eth0" # имя интерфейса на локальную сеть
IF_INET1="eth1" # имя интерфейса на первого провайдера.
IF_INET2="eth2" # имя интерфейса на второго провайдера.

NET_LOCAL="172.16.0.0/16" # локальная сеть.
NET_INET1="1.1.1.0/24" # адрес сети в которой гейт нашего первого провайдера.
NET_INET2="2.2.2.0/24" # адрес сети в которой гейт нашего второго провайдера.

GW1="1.1.1.1" # гейт первого провайдера.
GW2="2.2.2.1" # гейт второго провайдера.

ip route flush table T1 #обнуляем первую таблицу маршрутов
ip route flush table T2 #обнуляем вторую таблицу маршрутов

ip route add $NET_INET1 dev $IF_INET1 src $IP_INET1 table T1 #закидываем в первую таблицу инфу о сети первого провайдера
ip route add $NET_LOCAL dev $IF_LOCAL table T1 #закидываем в первую таблицу инфу о том, что у нас существует локальная сеть
ip route add $NET_INET2 dev $IF_INET2 table T1 #закидываем в первую таблицу инфу о том, что у нас существует еще одна сеть
ip route add 127.0.0.0/8 dev lo table T1 #закидываем в первую таблицу инфу о существовании лупбека
ip route add default via $GW_INET1 table T1 #закидываем в первую таблицу дефолтный гейт на первого провайдера

ip route add $NET_INET2 dev $IF_INET2 src $IP_INET2 table T2 #закидываем во вторую таблицу инфу о сети второго провайдера
ip route add $NET_LOCAL dev $IF_LOCAL table T2 #закидываем во вторую таблицу инфу о том, что у нас существует локальная сеть.
ip route add $NET_INET1 dev $IF_INET1 table T2 #закидываем во вторую таблицу инфу о том, что у нас существует еще одна сеть.
ip route add 127.0.0.0/8 dev lo table T2 #закидываем во вторую таблицу инфу о существовании лупбека.
ip route add default via $GW_INET2 table T2 #закидываем во вторую таблицу дефолтный гейт на второго провайдера
ip route add $NET_INET1 dev $IF_INET1 src $IP_INET1 #заполняем основную таблицу адресов. сеть на первого провайдера
ip route add $NET_INET2 dev $IF_INET2 src $IP_INET2 #заполняем основную таблицу адресов. сеть на второго провайдера
ip route add default via $GW_INET1 #заполняем основную таблицу адресов. дефолтный гейт.
ip rule delete table T1 #удаляем наши таблицы, если они присутсвуют в текущей конфигурации (вдруг мы просто перезапускаем скрипт?)
ip rule delete table T2 #аналогично для второй.
ip rule delete table T1 #и делаем это два раза, так как у нас два правила на каждую таблицу.
ip rule delete table T2 #аналогично для второй.

#а вот это важно для понимая! если понять что происходит в следующих строчках - то можно настраивать любые правила маршрутизации.

ip rule add from $IP_INET1 table T1 #все пакеты от ip адреса первого провайдера маршрутизировать по таблице T1
ip rule add from $IP_INET2 table T2 #все пакеты от ip адреса второго провайдера маршрутизировать по таблице T2
ip rule add fwmark 10 table T1 #все пакеты, которые имеют метку 10 маршрутизировать по таблице T1
ip rule add fwmark 11 table T2 #все пакеты, которые имеют метку 11 маршрутизировать по таблицу T2
ip route flush cache #ну и на последок очистить кеш
exit 0

В целом, понятно все, за исключением одного вопроса - что же это за
метка такая в последних строчках?

Тут мы обращаемся к утилите iptables.

Краткая теория. (полную и на русском языке можно, а вообще-то, даже
нужно, читать тут http://www.opennet.ru/docs/RUS/iptables/ )

Утилита iptables так же основана на таблицах. только таблиц в ней
фиксированно - три.

mangles - таблица используется для изменений заголовков пакетов.
filter - самая наша основаная таблица для файрвола - тут мы производим все блокировки, разрешения.
nat - таблица, как понятно из названия для ната.

Каждая таблица уже имеет некоторые предлопределенные цепочки, в которые
можно добавлять правила, а так же поддерживает создание пользовательские
цепочки.

Когда паркт попадает на интерфейс, и, его ловит iptables, то он проходит
следующие таблицы и цепочки, важные для нас:

1. таблица mangle цепочка PREROUTING
2. обработка маршрутизации.
3. таблица nat цепочка POSTROUTING
(в действительно пакет проходит еще и через другие таблицы и цепочки,
но, для планируемой маршрутизации они нафик не сдались - изучение этого
вопроса уже полностью на вашей совести =) ).

Скрипт для маршрутизации:
/etc/init.d/iptables

#!/bin/sh

IP_LOCAL="172.16.0.1" # адрес нашего маршрутизатора в локальной сети.
IP_INET1="1.1.1.2" # адрес нашего маршрутизатора в сети первого провайдера.
IP_INET2="2.2.2.2" # адрес нашего марщрутизатора в сети второго провайдера.

IF_LOCAL="eth0" # имя интерфейса на локальную сеть
IF_INET1="eth1" # имя интерфейса на первого провайдера.
IF_INET2="eth2" # имя интерфейса на второго провайдера.

NET_LOCAL="172.16.0.0/16" # локальная сеть.
NET_INET1="1.1.1.0/24" # адрес сети в которой гейт нашего первого провайдера.
NET_INET2="2.2.2.0/24" # адрес сети в которой гейт нашего второго провайдера.
NET_SUB1="172.16.0.0/24" # подсеть пользователей на первого провайдера
NET_SUB2="172.16.1.0/24" # подсеть пользователей на второго провайдера

GW1="1.1.1.1" # гейт первого провайдера.
GW2="2.2.2.1" # гейт второго провайдера.

IPT="/sbin/iptables"

$IPT -t nat -F #очищаем таблицу nat
$IPT -t mangle -F #очищаем таблицу mangle

#все пакеты, которые попали в цепочку PREROUTING таблицы mangle обрабатываем по двум правилам:
$IPT -t mangle -A PREROUTING -s $NET_SUB1 -d ! $NET_LOCAL -j MARK --set-mark 10 # все пакеты которые идут от $NET_SUB1 и не в $NET_LOCAL маркируем меткой 10
$IPT -t mangle -A PREROUTING -s $NET_SUB2 -d ! $NET_LOCAL -j MARK --set-mark 11 # все пакеты которые идут от $NET_SUB1 и не в $NET_LOCAL маркируем меткой 10

#все пакеты, которые попали в цепочку POSTROUTING таблицы nat обрабатываем по двум правилам:
$IPT -t nat -A POTROUTING -m mark --mark 10 -j MASQUERADE # натим все пакеты которые имеют метку 10
$IPT -t nat -A POTROUTING -m mark --mark 11 -j MASQUERADE # натим все пакеты которые имеют метку 11

exit 0

И так, что же происходит.
Пусть адрес пользователя 172.16.0.2

У пользователя прописан дефолтный гейт 172.16.0.1 - т.е. все пакеты
которые направлены не в локальную сеть пойдут туда.

На наш маршрутизатор приходит пакет от 172.16.0.2 и на адрес (к примеру) 4.4.4.4

1. пакет попадает в таблицу mangle в цепоку PREROUTING и там по правилу
"все пакеты от $NET_SUB1 и не в $NET_LOCAL" маркируется меткой 10 и
попускается дальше.

2. пакет попадает в правила маршрутизации и обрабатывается по правилу
"все пакеты, которые имеют метку 10 маршрутизировать по таблице T1".
попав в эту таблицу маршрутизатор отправляет пакет в дефолтный гейт для
этой таблицы, т.к. адрес 4.4.4.4 не подходит ни под одну сеть, которые
есть в этой таблице.

3. пакет попадает в таблицу nat в цепочку POSTROUTING и там по правилу
"натим все пакеты которые имеют метку 10" натится наружу.

Важный момент: метка пакета существует только внутри нашего
марщрутизатора. выходящий пакет уже не имеет меток.

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

В целом, все =)

Осталось только сделать наши скрипты исполняемыми и закинуть их на
автозагрузку.

Важно обратить внимание, что данный маршрутизатор будет маршрутизировать
пакеты в обе стороны, так что если кто-то из внешних сетей пропишет себе
ip нашей локальной сети, и укажет в качестве маршрутизатора ip нашего
маршрутизатора - он спокойно попадет к нам.

Но настройка файрвола - это уже второй вопрос, который в эту статью не
входит.

оригинал - http://www.opennet.ru/base/net/debian_multilink.txt.html

Трифонов Евгений aka ieroglif aka Sart

free-templates.ru