Linux маршрутизатор с несколькими внешними интерфейсами
Взято здесь, хорошая статья, но сервак помер, решил сохранить, а то мало ли что...
Содержание
Linux маршрутизатор с несколькими внешними интерфейсами
From: Трифонов Евгений aka ieroglif aka Sart <astahe@mail.ru.>
Subject: 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" - подсеть пользователей на второго провайдера $GW_INET1="1.1.1.1" - гейт первого провайдера. $GW_INET2="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" # адрес сети в которой гейт нашего второго провайдера. GW_INET1="1.1.1.1" # гейт первого провайдера. GW_INET2="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, то он проходит следующие таблицы и цепочки, важные для нас:
- таблица mangle цепочка PREROUTING
- обработка маршрутизации.
- таблица 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" # подсеть пользователей на второго провайдера GW_INET1="1.1.1.1" # гейт первого провайдера. GW_INET2="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_SUB2 и не в $NET_LOCAL маркируем меткой 11 #все пакеты, которые попали в цепочку POSTROUTING таблицы nat обрабатываем по двум правилам: $IPT -t nat -A POSTROUTING -m mark --mark 10 -j MASQUERADE # натим все пакеты которые имеют метку 10 $IPT -t nat -A POSTROUTING -m mark --mark 11 -j MASQUERADE # натим все пакеты которые имеют метку 11
echo "1" > /proc/sys/net/ipv4/ip_dynaddr echo "1" > /proc/sys/net/ipv4/ip_forward exit 0
И так, что же происходит.
Пусть адрес пользователя 172.16.0.2
У пользователя прописан дефолтный гейт 172.16.0.1 - т.е. все пакеты которые направлены не в локальную сеть пойдут туда.
На наш маршрутизатор приходит пакет от 172.16.0.2 и на адрес (к примеру) 4.4.4.4
- пакет попадает в таблицу mangle в цепоку PREROUTING и там по правилу "все пакеты от $NET_SUB1 и не в $NET_LOCAL" маркируется меткой 10 и попускается дальше.
- пакет попадает в правила маршрутизации и обрабатывается по правилу "все пакеты, которые имеют метку 10 маршрутизировать по таблице T1". попав в эту таблицу маршрутизатор отправляет пакет в дефолтный гейт для этой таблицы, т.к. адрес 4.4.4.4 не подходит ни под одну сеть, которые есть в этой таблице.
- пакет попадает в таблицу nat в цепочку POSTROUTING и там по правилу "натим все пакеты которые имеют метку 10" натится наружу.
Важный момент: метка пакета существует только внутри нашего марщрутизатора. выходящий пакет уже не имеет меток.
Сам факт подобной реализации маршрутизации возможен только из-за того, что когда пакет обрабатывается правилами маршрутизации метка существует, так как после пакет возвращается на обработку в таблицы iptables.
В целом, все =)
Осталось только сделать наши скрипты исполняемыми и закинуть их на автозагрузку.
Важно обратить внимание, что данный маршрутизатор будет маршрутизировать пакеты в обе стороны, так что если кто-то из внешних сетей пропишет себе ip нашей локальной сети, и укажет в качестве маршрутизатора ip нашего маршрутизатора - он спокойно опадет к нам.
Но настройка файрвола - это уже второй вопрос, который в эту статью не входит.