Linux ppp-oe (ADSL) Balance

Материал из OpenWiki
Версия от 06:53, 14 октября 2008; ImportUser (обсуждение)

(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск
Условия:

Есть два канала к разным провайдерам (ADSL) с динамическим выделением адресов. Есть локальная офисная сеть. Есть шлюз под Linux.

Задача:

Необходимо обеспечить бесперебойный выход в интернет локалки с распределением нагрузки на оба канала и специфической маршрутизацией к сетям провайдеров.

Аппаратное обеспечение:
Шлюз локальной сети в виде простенького старенького компьютера с установленным Linux с установленными пакетами iptables, rp-pppoe, iproute2 и тремя сетевыми картами - eth0 - локальная сеть, eth1 и eth2 соответственно к провайдерам (в ADSL модемы или кабельную сеть с организацией связи по протоколу
ppp-oe
).
Решение:

Тестировалось на 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

Пока все. Скрипт работает, тестирую во всевозможных ситуациях... По мере нахождения багов буду исправлять.