Linux ppp-oe (ADSL) Balance
Содержание
Условия:
Есть два канала к разным провайдерам (ADSL) с динамическим выделением адресов. Есть локальная офисная сеть. Есть шлюз под Linux.
Задача:
Необходимо обеспечить бесперебойный выход в интернет локалки с распределением нагрузки на оба канала и специфической маршрутизацией к сетям провайдеров.
Аппаратное обеспечение:
Шлюз локальной сети в виде простенького старенького компьютера с установленным Linux с установленными пакетами iptables, rp-pppoe, iproute2 и тремя сетевыми картами - eth0 - локальная сеть, eth1 и eth2 соответственно к провайдерам (в ADSL модемы или кабельную сеть с организацией связи по протоколу PPPoE).
Решение:
Тестировалось на ASPLinux 11.2, но вполне применимо с небольшими изменениями и к другим дистрибутивам.
Модемы переводим в режимbridge- т.е.
ppp-oeсессии у нас будет поднимать наш шлюз. Считаем что интерфейс eth0 в локальную сеть уже был настроен. Создаем интерфейсы ppp при помощи утилиты
adsl-setup, следуя указаниям на экране. Интерфейсы eth1 и eth2 настраивать нет необходимости - главное проверить что они присутствуют в выводе команды
lspci:
# lspci|grep Ethernet 00:09.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+ (rev 10) 00:0a.0 Ethernet controller: Intel Corporation 82557/8/9 [Ethernet Pro 100] (rev 08) 00:0c.0 Ethernet controller: Intel Corporation 82557/8/9 [Ethernet Pro 100] (rev 08)
И для них загружены соответствующие модули ядра:
# lsmod|grep mii mii 5569 3 8139cp,8139too,e100
После создания интерфейсов ppp мы наблюдаем такую картину:
# ls /etc/sysconfig/network-scripts|grep ifcfg ifcfg-eth0 ifcfg-lo ifcfg-ppp0 ifcfg-ppp1Требуется отредактировать файлы конфигурации интерфейсов, созданные при помощи утилиты
adsl-setup, приведя их к такому виду:
# cat /etc/sysconfig/network-scripts/ifcfg-ppp0 LCP_INTERVAL=20 FIREWALL=NONE PEERDNS=yes PROVIDER=DSLppp0 LCP_FAILURE=3 BROADCAST="" ETH=eth2 # У вас может отличаться CLAMPMSS=1412 DEMAND=no USERCTL=no USER=username1prov1 # Изменить на реальное имя (login) CONNECT_TIMEOUT=0 CONNECT_POLL=6 BOOTPROTO=none NAME=DSLppp0 TYPE=xDSL DEVICE=ppp0 SYNCHRONOUS=no MTU=1492 NETMASK="" IPADDR="" DEFROUTE=no # Будем задавать позже NETWORK="" PING=. PPPOE_TIMEOUT=30 # Уменьшить время ONBOOT=yes PIDFILE=/var/run/pppoe-ppp0.pid # Необходимо разнести pid файл
и второй интерфейс:
# cat /etc/sysconfig/network-scripts/ifcfg-ppp1 LCP_INTERVAL=20 FIREWALL=NONE PEERDNS=yes PROVIDER=DSLppp1 LCP_FAILURE=3 BROADCAST="" ETH=eth1 # У вас может отличаться CLAMPMSS=1412 DEMAND=no USERCTL=no USER=username2prov2 # Изменить на реальное имя (login) CONNECT_TIMEOUT=0 CONNECT_POLL=6 BOOTPROTO=none NAME=DSLppp1 TYPE=xDSL DEVICE=ppp1 SYNCHRONOUS=no MTU=1492 NETMASK="" IPADDR="" DEFROUTE=no # Будем задавать позже NETWORK="" PING=. PPPOE_TIMEOUT=30 # Уменьшить время ONBOOT=yes PIDFILE=/var/run/pppoe-ppp1.pid # Необходимо разнести pid файлДля информации - логины и пароли для доступа находятся в файлах
/etc/ppp/chap-secretsили
/etc/ppp/pap-secrets(зависит от типа авторизации у провайдера).
Формат этих файлов следующий:
# Secrets for authentication using CHAP (or PAP) # client server secret IP addresses "username1prov1" * "passwd1prov1" * # Провайдер1 "username2prov2" * "passwd2prov2" * # Провайдер2Проверим что
ppp-oeсоединение устанавливается -
ifup ppp0затем
ifup ppp1- посмотрим вывод
ifconfig. Все в порядке? IP от провайдеров получены? Опускаем интерфейсы -
ifdown ppp0затем
ifdown ppp1. Идем дальше. Для наведения порядка и присваивания каждому
ppp-oeинтерфейсу фиксированного имени необходимо изменить файл
/etc/ppp/ip-up, приведя его к виду
#!/bin/bash # This file should not be modified -- make local changes to # /etc/ppp/ip-up.local instead PATH=/sbin:/usr/sbin:/bin:/usr/bin export PATH LOGDEVICE=$6 REALDEVICE=$1 # Фиксируем имена ppp-oe (DSL) интерфейсов добавляя префикс dsl NEWNAME="dsl${LOGDEVICE}" [ -z "${LOGDEVICE}" ] && NEWNAME="$1" if [ -x /sbin/ip ]; then /sbin/ip link set $1 down /sbin/ip link set $1 name ${NEWNAME} /sbin/ip link set ${NEWNAME} up fi sleep 3 [ -f /etc/sysconfig/network-scripts/ifcfg-${LOGDEVICE} ] && /etc/sysconfig/network-scripts/ifup-post ifcfg-${LOGDEVICE} /etc/ppp/ip-up.ipv6to4 ${LOGDEVICE} [ -x /etc/ppp/ip-up.local ] && /etc/ppp/ip-up.local "$@" exit 0После этого наши ppp интерфейсы в выводе команды
ifconfigбудут иметь имена dslppp0 и dslppp1 соответственно. Далее необходимо изменить файл
/etc/sysconfig/network-scripts/ifup-post, вставив в него в самом конце перед последней строчкой
exit 0следующее:
sleep 3 if [ "${DEVICETYPE}" = "ppp" -o "${DEVICETYPE}" = "ippp" ]; then /etc/sysconfig/route/balance fi
Определимся для себя, где будем хранить скрипт отслеживающий состояние интерфейсов и в соответствии с ним задающий маршрутизацию.
К примеру создаем каталогmkdir -p /etc/sysconfig/route. Кроме скрипта в нем будет находиться еще несколько служебных файлов. Далее необходимо изменить файл
/etc/sysconfig/network-scripts/ifup-post, вставив в него в самом конце перед последней строчкой
exit 0следующее:
if [ "${DEVICETYPE}" = "ppp" -o "${DEVICETYPE}" = "ippp" ]; then /etc/sysconfig/route/balance fiДобавляем таблицы в файл
/etc/iproute2/rt_tables- получается примерно такое:
# reserved values # #255 local #254 main #253 default #0 unspec # # local # #1 inr.ruhep # Ниже наши два провайдера 1 alpha 2 utel
Создадим скрипт, в котором будем указывать статические маршруты к DNS серверам и сетям провайдеров (ведь так удобней? и быстрее...) и к некоторым другим. Если не указать эти маршруты, то может возникнуть затруднение с резолвингом имен вашим кеширующим DNS сервером и соответственно долгий отклик удаленных хостов.
Выполним командуtouch /etc/sysconfig/route/providers.lan; chmod 744 /etc/sysconfig/route/providers.lanСкрипт примерно такого содержания:
#!/bin/bash GW1=`/sbin/ifconfig dslppp0|grep "inet addr"|awk '{print $3}'|cut -d ':' -f 2` # Alpha GW2=`/sbin/ifconfig dslppp1|grep "inet addr"|awk '{print $3}'|cut -d ':' -f 2` # Utel # For Utel - сеть одного провайдера # Изменить под себя!!! route $1 -net 82.207.0.0/16 gw $GW2 route $1 -net 91.124.0.0/16 gw $GW2 route $1 -net 195.5.0.0/16 gw $GW2 # For Alpha - сеть другого провайдера # Изменить под себя!!! route $1 -host 143.140.20.144 gw $GW1 # DNS server route $1 -host 192.168.10.111 gw $GW1 route $1 -host 192.168.20.113 gw $GW1 route $1 -host 192.168.21.203 gw $GW1 route $1 -host 192.168.11.8 gw $GW1 route $1 -net 143.140.23.0/24 gw $GW1 # For ICQ - нестабильно работает через nexthop, приходится задавать маршрут жестко :( # Изменить под себя возможно потребуется только $GW2 route $1 -net 64.12.0.0/16 gw $GW2 route $1 -net 205.188.0.0/16 gw $GW2 route $1 -net 152.163.0.0/24 gw $GW2 # For мои любимые хосты или сети с играми, и т.д - сколько угодно... route $1 -host 213.28.61.21 gw $GW1 route $1 -host 92.73.6.11 gw $GW2 route $1 -net 13.33.15.0/24 gw $GW1Создаем сам скрипт
touch /etc/sysconfig/route/balance; chmod 744 /etc/sysconfig/route/balanceсо следующим содержанием:
(в данном примере скрипт ориентирован на провайдера у которого есть доступ во всю сеть кроме UA-IX, и провайдера с полным доступом, при этом каналы и ценовая политика их одинакова. Отредактировать его так, чтобы к примеру разделять доступ в Российские сети через одного провайдера, а на мир делить поровну - труда не представляет.)
#!/bin/bash sleep 5 # Задаем интерфейсы и локальную сеть IFIN=eth0 # Local Network IFOUT1=dslppp0 # Alpha - есть доступ в сеть UA-IX IFOUT2=dslppp1 # Utel - доступа в сеть UA-IX нет LOCALNET=192.168.0.0/24 # Local network # Определяем IP и GATEWAY наших интерфейсов IPIN=`ifconfig ${IFIN}|grep "inet addr"|awk '{print $2}'|cut -d ':' -f 2` IPOUT1=`ifconfig ${IFOUT1}|grep "inet addr"|awk '{print $2}'|cut -d ':' -f 2` GW1=`ifconfig ${IFOUT1}|grep "inet addr"|awk '{print $3}'|cut -d ':' -f 2` IPOUT2=`ifconfig ${IFOUT2}|grep "inet addr"|awk '{print $2}'|cut -d ':' -f 2` GW2=`ifconfig ${IFOUT2}|grep "inet addr"|awk '{print $3}'|cut -d ':' -f 2` # Задаем таблицы маршрутизации TABLE1=alpha TABLE2=utel # Принимаем за правило что интерфейс в локалку (eth0) всегда UP, поэтому проверяем наличие только ppp интерфейсов. # Если поднят только какой-то один ppp - задаем его шлюз по умолчанию if [[ `ip link show ${IFOUT1}|grep "UP"|awk '{print $3}'|cut -d ',' -f 4` != `ip link show ${IFOUT2}|grep "UP"|awk '{print $3}'|cut -d ',' -f 4` ]]; then if [ `ip link show ${IFOUT1}|grep "UP"|awk '{print $3}'|cut -d ',' -f 4` ] && [ "UP" ];then # Если не подняты оба интерфейса, а поднят только dslppp0 то удаляем принудительную маршрутизацию в dslppp1 # /sbin/route del default gw ${GW2} bash /etc/sysconfig/route/ua-ix.lan del /etc/sysconfig/route/providers.lan del # и делаем маршрут по умолчанию через живой dslppp0 интерфейс /sbin/route add default gw ${GW1} &> /dev/null else if [ `ip link show ${IFOUT2}|grep "UP"|awk '{print $3}'|cut -d ',' -f 4` ] && [ "UP" ];then # Если не поднят dslppp0 а поднят dslppp1 интерфейс то делаем маршрут по умолчанию через живой dslppp1 интерфейс #/sbin/route del default gw ${GW1} bash /etc/sysconfig/route/ua-ix.lan del /etc/sysconfig/route/providers.lan del /sbin/route add default gw ${GW2} &> /dev/null fi fi fi # Проверяем если подняты оба ppp интерфейса - делаем маршрутизацию через них с балансировкой нагрузки if [[ `ip link show ${IFOUT1}|grep "UP"|awk '{print $3}'|cut -d ',' -f 4` != `ip link show ${IFOUT2}|grep "UP"|awk '{print $3}'|cut -d ',' -f 4` ]]; then : else # Удаляем статические таблицы маршрутизации по умолчанию /sbin/route del default gw ${GW1} &> /dev/null /sbin/route del default gw ${GW2} &> /dev/null bash /etc/sysconfig/route/ua-ix.lan del /etc/sysconfig/route/providers.lan del # Описываем через какие таблицы кто ходит /sbin/ip route add ${LOCALNET} via ${IPIN} table ${TABLE1} &> /dev/null /sbin/ip route add ${LOCALNET} via ${IPIN} table ${TABLE2} &> /dev/null /sbin/ip route add 127.0.0.0/8 dev lo table ${TABLE1} &> /dev/null /sbin/ip route add 127.0.0.0/8 dev lo table ${TABLE2} &> /dev/null /sbin/ip route add 0/0 via ${GW1} table ${TABLE1} &> /dev/null /sbin/ip route add 0/0 via ${GW2} table ${TABLE2} &> /dev/null # Задание правил маршрутизации по IP-адресу источника /sbin/ip rule add from ${IPOUT1} lookup ${TABLE1} /sbin/ip rule add from ${IPOUT2} lookup ${TABLE2} # Распределяем нагрузку на два канала /sbin/ip route add default equalize nexthop via ${GW1} dev ${IFOUT1} weight 1 nexthop via ${GW2} dev ${IFOUT2} weight 1 &> /dev/null # Получаем и обрабатываем список Украинских сетей (UA-IX) - на выбор два источника # Эту часть можно вынести в отдельный файл и запускать по cron - кому как удобнее #/usr/bin/wget -q --no-cache -O /etc/sysconfig/route/uaix.lan http://noc.mirotel.net/uaix.nets.txt - С некоторых пор эта ссылка не работает /usr/bin/wget -q --no-cache -O /etc/sysconfig/route/uaix.lan http://www.colocall.net/ua/prefixes.txt # Гы-гы... следующие две строки коряво сделаны... может кто подправит? /usr/bin/perl -pi.bak -e "s/^(.*)$/route add -net \$1 gw $GW1/" /etc/sysconfig/route/uaix.lan /usr/bin/replace 'add' '$1' < /etc/sysconfig/route/uaix.lan > /etc/sysconfig/route/ua-ix.lan rm -f /etc/sysconfig/route/uaix.* # Задаем маршрутизацию в UA-IX через нужный интерфейс - в нашем случае dslppp0 bash /etc/sysconfig/route/ua-ix.lan add # Задаем маршрутизацию к DNS серверам и сетям провайдеров через их интерфейсы /etc/sysconfig/route/providers.lan add fi exit 0
Пока все. Скрипт работает, тестирую во всевозможных ситуациях... По мере нахождения багов буду исправлять.