пятница, 20 марта 2009 г.

использование двух каналов инет

Есть у меня своя домашняя сеть, с linux сервером, и подключена она к интернет с помощью беспроводного соединения — на крыше антена и роутер, к серверу подключено витой парой. Все вобщем то неплохо, канал с гарантированой полосой в обоих направлениях, постоянный IP адрес, довольно надежный — падает редко. Но вот есть у него один минус — цена кусается.
Ценовая политика провайдера построена так, что для того, чтоб увеличить скорость в два раза — платить тоже надо в два раза больше. А скорости хочется больше! И надежности тоже — как то во время сильных заморозков роутеру стало «холодно» и интернета вечером и ночью небыло.
Поэтому задумал я провести домой второй интернет-канал, выбар пал на одного известного на Украине провайдера, предоставляющего доступ по ADSL. У него и тарифы недорогие и модем ADSL стоит недорого. Так я и сделал, подключился, воткнул ADLS модем в свич — все работает. Но от старого доброго беспроводного канала отказываться мне нехотелось, поэтому задумал я сделать так, чтоб интернет трафик шел сразу по обеим каналам, так, чтоб я мог воспользоваться суммарной пропускной способностью. Да еще и чтоб при падении одного канала всю нагрузку на себя брал другой.



После поиска в интернете я выснил, что есть как минимум два решения:

— на уровне файрвола раскидывать TCP сессии по разным интерфейсам. Недостатки — сайты, которые имею привязку сессий к IP адресу перестанут работать, так как последовательные запросы от одного пользователя могут приходить по разным каналам и с разных IP.
— на уровне маршрутизации раскидывать маршруты через разные интерфейсы. Проблем перового решения небудет, так как маршруты кешируются и последующие обращения к одному адресу будут идти через один и тот же интерфейс. Но балансировка будет не такая точная, и качая с одного сервера даже в несколько потоков не удастся достигнуть суммарной скорости двух каналов.

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

Итак, приступим!

Для начала определим переменные:
$ cat /etc/balance/vars

1. #!/bin/bash
2.
3. # LAN interface
4. IF0="eth1"
5.
6. # WAN interface 1
7. IF1="eth0"
8.
9. # WAN interface 2
10. IF2="ppp0"
11.
12. IP1="194.9.xx.xx"
13. IP2="`ip addr show $IF2 | grep inet | awk '{print $2}'`"
14.
15. # gateway 1
16. P1="194.9.xx.xx"
17. # gateway 2
18. P2="195.5.xx.xx"
19.
20. # LAN netmask
21. P0_NET="192.168.0.0/24"
22. # WAN1 netmask
23. P1_NET="194.9.xx.xx/xx"
24. # WAN2 netmask
25. P2_NET="195.5.xx.xx/xx"
26.
27.
28. TBL1="provider1"
29. TBL2="provider2"
30.
31. # Realtive weight of channels bandwidth
32. W1="2"
33. W2="1"




Добавим в файл /etc/iproute2/rt_tables две дополнительные таблицы маршрутизации:
echo "1 provider1" >> /etc/iproute2/rt_tables
echo "2 provider2" >> /etc/iproute2/rt_tables


Теперь напишем скрипт, который будет прописывать все необходимые маршруты и правила файрвола:

cat /etc/balance/routing.sh

1. #!/bin/bash
2.
3. . /etc/balance/vars
4.
5. echo "1" > /proc/sys/net/ipv4/ip_forward
6.
7.
8. ip route add $P1_NET dev $IF1 src $IP1 table $TBL1 > /dev/null 2>&1
9. ip route add default via $P1 table $TBL1 > /dev/null 2>&1
10. ip route add $P2_NET dev $IF2 src $IP2 table $TBL2 > /dev/null 2>&1
11. ip route add default via $P2 table $TBL2 > /dev/null 2>&1
12.
13. ip route add $P1_NET dev $IF1 src $IP1 > /dev/null 2>&1
14. ip route add $P2_NET dev $IF2 src $IP2
15.
16. ip route add default via $P1 > /dev/null 2>&1
17.
18. ip rule add from $IP1 table $TBL1 > /dev/null 2>&1
19. ip rule add from $IP2 table $TBL2 > /dev/null 2>&1
20.
21.
22. ip route add $P0_NET dev $IF0 table $TBL1 > /dev/null 2>&1
23. ip route add $P2_NET dev $IF2 table $TBL1 > /dev/null 2>&1
24. ip route add 127.0.0.0/8 dev lo table $TBL1 > /dev/null 2>&1
25. ip route add $P0_NET dev $IF0 table $TBL2 > /dev/null 2>&1
26. ip route add $P1_NET dev $IF1 table $TBL2 > /dev/null 2>&1
27. ip route add 127.0.0.0/8 dev lo table $TBL2 > /dev/null 2>&1
28.
29. iptables -t nat -F POSTROUTING
30. iptables -t nat -A POSTROUTING -s $P0_NET -o $IF1 -j MASQUERADE
31. iptables -t nat -A POSTROUTING -s $P0_NET -o $IF2 -j MASQUERADE



Этот набор команд обеспечивает маршрутизацию ответов через интерфейс, на котором был получен запрос, а так же маскарадинг а обоих интерфейсах.

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

$ cat /etc/balance/check.sh

1. #!/bin/bash
2.
3. . /etc/balance/vars
4.
5. OLDIF1=0
6. OLDIF2=0
7.
8. . /etc/balance/routing.sh
9. while true; do
10.
11.
12. ping -c 3 -s 100 $P1 -I $IF1 > /dev/null
13. if [ $? -ne 0 ]; then
14. echo "Failed IF1!"
15. NEWIF1=0
16. else
17. NEWIF1=1
18. fi
19.
20. ping -c 3 -s 100 $P2 -I $IF2 > /dev/null
21. if [ $? -ne 0 ]; then
22. echo "Failed IF2!"
23. NEWIF2=0
24. else
25. NEWIF2=1
26. fi
27.
28. if (( ($NEWIF1!=$OLDIF1) || ($NEWIF2!=$OLDIF2) )); then
29. echo "Changing routes"
30.
31. if (( ($NEWIF1==1) && ($NEWIF2==1) )); then
32. echo "Both channels"
33. ip route delete default
34. ip route add default scope global nexthop via $P1 dev $IF1 weight $W1 \
35. nexthop via $P2 dev $IF2 weight $W2
36. elif (( ($NEWIF1==1) && ($NEWIF2==0) )); then
37. echo "First channel"
38. ip route delete default
39. ip route add default via $P1 dev $IF1
40. elif (( ($NEWIF1==0) && ($NEWIF2==1) )); then
41. echo "Second channel"
42. ip route delete default
43. ip route add default via $P2 dev $IF2
44. fi
45.
46. else
47. echo "Not changed"
48. fi
49.
50. OLDIF1=$NEWIF1
51. OLDIF2=$NEWIF2
52. sleep 3
53. done



Работу канала проверяем пингуя шлюз, и если нет ответа на 3 пинга подряд — мы считаем, что канал упал, и соответственно исключаем его из таблицы маршрутизации.

Таким образом, если работают оба канала:

$ ip route
195.5.xx.xx dev ppp0 proto kernel scope link src 95.133.xx.xx
194.9.xx.xx/xx dev eth0 proto kernel scope link src 194.9.xx.xx
192.168.0.0/24 dev eth1 proto kernel scope link src 192.168.0.75
default
nexthop via 194.9.xx.xx dev eth0 weight 2
nexthop via 195.5.xx.xx dev ppp0 weight 1


Итого имеем два default gw, первый с весом 2 и второй с весом 1. Тоесть через первый канал пойдет в два раза больше трафика, чем через второй.

Для того, чтобы кастомизировать эти скрипты под ваши нужды необходимо настроить значения в файле vars, остальные скрипты практически не требуют настройки.



Здесь я хочу рассказать о настройке шлюза на Linux'e, для использования 2-х (и более) провайдеров интернета.
Для настройки мы будем использовать возможности iptables и утилиты ip из пакета, который как правило называется iproute2. А для решения поставленной задачи пакеты мы будем маршрутизировать на основе "policy routing" (т.е. маршрутизация на основе политик), а не "destination routing" (маршрутизация на основе адреса получателя).
Итак, приступим. Для начала определимся с переменными:


#!/bin/bash

IF1=eth1
IF2=eth2


IF - это сетевые интерфейсы, которые смотрят в интернет, через наших провайдеров


IP1=10.10.10.10
IP2=20.20.20.20


IP - это наши внешние IP-адреса, которые нам выдали провайдеры


P1=10.10.10.1
P2=20.20.20.1


P - это шлюзы по умолчанию у наших провайдеров
Policy routing позволяет выполнять маршрутизацию на основе адреса источника поэтому перечислим сервера которые будут учавствовать:


SRV11=192.168.0.11
SRV12=192.168.0.12


Здесь SRV11 и SRV12 - это два айпишника одного и тогоже сервера (это важно!), это позволяет одному серверу обрабатывать входящие соединения с двух провайдеров. Конечно же, существуют и другие варианты реализовать эту возможность, но я буду использовать именно айпишники, мне кажется для начала так будет проше.
Ну а теперь самое интересное - пишем правило для маршрутизации.
Первое что мы должны сделать это добавить свои таблицы маршрутизации, для этого необходимо отредактировать файл /etc/iproute2/rt_tables, например так:


#echo "101 T1" >> /etc/iproute2/rt_tables
#echo "102 T2" >> /etc/iproute2/rt_tables


Заполняем первую таблицу:


ip route add $P1_NET dev $IF1 src $IP1 table T1
ip route add default via $P1 table T1


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


ip route add $P2_NET dev $IF2 src $IP2 table T2
ip route add default via $P2 table T2


Затем разберемся с основной таблицей, которая называется "main". Ее мы видим, когда набираем ip route:


ip route add $P1_NET dev $IF1 src $IP1
ip route add $P2_NET dev $IF2 src $IP2
ip route add default via $P1 metric 10


Первые две строчки аналогичны предыдущим записям, только опущено "table main". В третьей строчке задается маршрут по умолчанию с указанием метрики.
На этом с маршрутизацией разобрались, чтобы посмотреть что у нас находится в таблице маршрутизации можно выполнить команду "ip route show table <имя таблицы>". Теперь приступим к правилам. Как раз по правилам и будет приниматься решения какой пакет по какой таблице будет маршрутизироваться.


ip rule add from $IP1 table T1
ip rule add from $IP2 table T2


Здесь мы указали, что если адрес источника равен первому внешнему адресу, тогда маршрутизация выполняется по таблице T1. Аналогично вторая запись.
И наконец самое интересное:


ip rule add from $SRV11 fwmark 10 table T1
ip rule add from $SRV12 fwmark 20 table T2

Используя iptables мы можем маркировать интересующие нас пакеты и маршрутизировать их на основе этих меток. Собственно здесь мы добавили два правила: для пакетов, имеющих метку 10, использовать таблицу T1, для пакетов с меткой 20 - T2. Сейчас возможно не очень понятно для чего это может потребоваться, но из правил iptables все станет ясно. Для просмотра правил выполняем "ip rule", при маршрутизации они проверяются по порядку.
Ну вот половина работы сделана осталось написать правила для iptables, об этом мы поговорим во второй части.

Комментариев нет:

Отправить комментарий