Pull to refresh

Объединение пропускной способности двух интернет каналов и простая отказоустойчивость

Reading time 5 min
Views 83K
Есть у меня своя домашняя сеть, с 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, остальные скрипты практически не требуют настройки.
Tags:
Hubs:
+95
Comments 106
Comments Comments 106

Articles