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

Материал из rrv-wiki
Перейти к навигации Перейти к поиску

Взято здесь, хорошая статья, но сервак помер, решил сохранить, а то мало ли что...

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, то он проходит следующие таблицы и цепочки, важные для нас:

  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" # подсеть пользователей на второго провайдера

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

  1. пакет попадает в таблицу mangle в цепоку PREROUTING и там по правилу "все пакеты от $NET_SUB1 и не в $NET_LOCAL" маркируется меткой 10 и попускается дальше.
  2. пакет попадает в правила маршрутизации и обрабатывается по правилу "все пакеты, которые имеют метку 10 маршрутизировать по таблице T1". попав в эту таблицу маршрутизатор отправляет пакет в дефолтный гейт для этой таблицы, т.к. адрес 4.4.4.4 не подходит ни под одну сеть, которые есть в этой таблице.
  3. пакет попадает в таблицу nat в цепочку POSTROUTING и там по правилу "натим все пакеты которые имеют метку 10" натится наружу.

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

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

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

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

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

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