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
Пока все. Скрипт работает, тестирую во всевозможных ситуациях... По мере нахождения багов буду исправлять.