Pull to refresh

Отказоустойчивый узел передачи данных

Reading time 12 min
Views 26K
Каждый оператор ШПД думает о том, как выпускать пользователей в сеть интернет и грамотно ограничивать скорость работы в сети по имеющимся тарифным планам и иметь резерв на случай отказа оборудования или работ связанных с отключением оборудования. Попытаюсь рассказать и показать на примере то, как это реализовано у нас (к нам подключены более 3х тысяч пользователей и описанный мною вариант работает очень даже неплохо)


Для начала нам дано:
1) 1U rack mount сервер (Intel Xeon E5335, 1GB Ram, двухпортовый NIC Intel PRO/1000 EB), у нас таких используется два. Для чего – расскажу в тексте статьи
2) Пограничный маршрутизатор (в моем случае Juniper j4350, 2 штуки)
3) L3 коммутатор поддерживающий протокол BGP или OSPF
4) А также задачу: A) Имея все это хозяйство выпустить пользователей в Интернет.
Б) сделать резерв, который в случае аварии будет задействован
автоматически и без нашего участия

Пограничный маршрутизатор (рекомендации)

Начнем с с настройки пограничного маршрутизатора.
Он может быть любой, хоть PC под FreeBSD/Linux со всем известным пакетом Quagga, а может быть оборудованием производителей Cisco или Juniper. Как настроить пограничный маршрутизатор на PC c пакетом quagga или cisco очень много статей в интернете и много подробной документации (да, конфиг кваги идентичен конфигу cisco). По джуниперу тоже есть информация, не так много, но есть. Да и тема статьи не про настройку пограничного маршрутизатора и протокола BGP на нем, а немного про другое.

В моем случае в качестве пограничных маршрутизаторов используются два Juniper j4350, железка не очень мощная, но под текущие задачи и бюджет подходит как раз.
Каждый из этих маршрутизаторов установлен на своем узле (узлы в разных местах) и к каждому из них подключен свой аплинк. Со стороны аплинков по bgp протоколу мы принимаем full-view и анонсируем им свои сети. А на шейперы мы отдаем по внутреннему bgp, в специально отведенном для этого vlan-e только маршрут по умолчанию (default route)
Для джунипера это строчки в конфиге:
Вprotocols bgp
neighbor 195.xxx.xxx.226 { export [ default-originate reject ];

В policy options
policy-statement default-originate {
from {
route-filter 0.0.0.0/0 exact;
}
then accept;
}, это чтобы отдать только маршрут по умолчанию
policy-statement reject {
then reject;
} , а это чтобы отбросить все остальные маршруты.


Пограничные маршрутизаторы настроили, каждый принимает full-view от аплинков и отдает наши сети. Теперь настраиваем vlan для внутреннего bgp, внутри него подымаем связь по протоколу bgp между пограничными маршрутизаторами для обмена маршрутами между ними. Выделяем IP адреса будущим шейперами, но желательно не один, а несколько, для пула внешних IP адресов используемых для NAT. В моем случае по 4-ре адреса на каждый шейпер, но вы можете больше или меньше. В зависимости от размера сети и количества абонентов.

Настройка сервера для шейпирования и NAT на FreeBSD

Достаем сервер из коробки, и начинаем устанавливать на него FreeBSD, лучше последней стабильной версии. Устанавливать FreeBSD желательно в самой минимальной комплектации, не забыв отметить порты и дерево исходных кодов ядра (после настройки можно удалить)

После того как поставили FreeBSD, начинаем настраивать на ней сеть. В /etc/resolv.conf задаем адреса dns серверов и прописываем маршрут по умолчанию и IP адреса в файл /etc/rc.conf (не смотря на наличие bgp маршрут по умолчанию лишним никогда не будет, на случай проблем с квагой которую мы будем ставить далее)
------/etc/rc.conf----
defaultrouter="195.xxx.xxx.225" #маршрут по умолчанию
gateway_enable="YES" #включаем режим пересылке пакетов между интерфейсами
hostname="gw2.xxx.ru" #задаем имя маршрутизатора
# em0 – внешний интерфейс в влане c внутренним bgp
ifconfig_em0="up"
ifconfig_em0="inet 195.xxx.xx.231 netmask 255.255.255.240" #освновной IP адрес
ifconfig_em0_alias0="inet 195.xxx.xxx.232 netmask 255.255.255.255" #дополнительные IP.
ifconfig_em0_alias1="inet 195.xxx.xxx.233 netmask 255.255.255.255" #алиасы
ifconfig_em0_alias2="inet 195.xxx.xxx.234 netmask 255.255.255.255"
#em1 – внутренняя сеть, в моем случае это vlan между шейперами и l3 коммутаторами
ifconfig_em1="up"
ifconfig_em1="inet 195.xxx.xxx.193 netmask 255.255.255.248"

— Сеть поднялась, яндекс запинговался. Теперь обновляем дерево портов
Дерево портов можно обновить с использованием утилиты portsnap или csup
В нашем случае будем обновлять через portsnap. Для этого пишем команды:
portsnap fetch, это загрузит полное дерево портов в каталог /var/db/portsnap
portsnap extract, этой командой мы распакуем новое дерево портов в каталог /usr/ports

После обновления портов имеет смысл заняться сборкой своего собственного ядра, хотя делать это не обязательно, ipfw, dummynet и pf можно подгрузить модулями, а на производительность пересобранное ядро особо не влияет. В нашем случае пересборка ядра нужна из за опции HZ, это интервал таймера. По умолчанию там стоит 100, что не очень подходит для больших скоростей, поэтому будем менять его на 2000

Копируем дистрибутивный конфиг ядра в другой файл
cp /usr/src/sys/i386/conf/GENERIC /usr/src/sys/i386/conf/router-001
открываем файл router-001 текстовым редактором (vi, nano) и начинаем его править. После правки ядра под ваши нужды надо не забыть добавить в конфиг ядра следующие параметры:

#ipfw firewall , этим мы будем шейпить пользователей
options IPFIREWALL
options IPFIREWALL_VERBOSE
options IPFIREWALL_VERBOSE_LIMIT=100
options IPFIREWALL_FORWARD
options DUMMYNET
options HZ=2000

# pf, а этим мы будем NAT-ить локальные IP
device pf
device pflog
device pfsync


Далее командами make buildkernel и make installkernel мы собираем и устанавливаем ядро

После сборки ядра переходим к настройке нашего файрвола ipfw
В файл /etc/rc.conf добавляем следующие строки:
firewall_enable="YES"
pf_enable="YES"
pf_rules="/etc/pf.conf"

полностью очищаем файл /etc/rc.firewall и начинаем писать в него свои правила, ничего сложного нету так-так rc.firewall представляет собой самый обычный shell скрипт который запускается при старте ipfw

строки которые добавляем в новый /etc/rc.firewall
#!/bin/sh , указываем путь к командному интерпретатору

/sbin/ipfw -q flush
/sbin/ipfw -q pipe flush , очищаем правила файрвола и очереди dummynet при старте
fwcmd="/sbin/ipfw -q" , Задаем путь к ipfw и опцию с которой запускаться. –q говорит файрволу о том, чтобы он не спрашивал подтверждения

WAN_IP = 195.xxx.xxx.231, указываем основной IP адрес внешнего интерфейса
IBGP_NET = 195.xx.xx.224/xx
LAN_IP = 10.249.0.0/16 , указываем диапазон наших внутренних сетей

${fwcmd} add 10 allow ip from any to any via lo0 , задаем правило разрешающее все на
loopback интерфейсe

${fwcmd} add 11 allow ip from me to me , разрешаем пакеты с этого хоста на этот хост

${fwcmd} add 12 allow icmp from any to me, разрешаем icmp на этот хост

${fwcmd} add 20 allow tcp from table(1) to me dst-port 22 , разрешаем подключаться к шейперу
по протоколу ssh с IP адресов указанных в таблице 1. Занести адрес в эту таблицу можно
командой ipfw table 1 add <ip_address>

${fwcmd} add 21 deny tcp from any to me dst-port 22 , окончательно запрещаем ssh. Под это
правило попадут все, кто не в таблице 1

${fwcmd} add 30 pipe tablearg ip from any to table(2) out via em1
${fwcmd} add 31 pipe tablearg ip from table(3) to any in via em1 , выпускаем пользователей из таблиц 2 и 3 в интернет и указываем им конкретный пайп дамминета для шейпинга. Номер пайпа указываем при добавлении пользователя в таблицу, каждая таблица для определенного типа трафика (2 для входящего к клиенту, 3 – для исходящего). Добавление пользователя в таблицу происходит командами ipfw table 2 add <ip клиента> <номер пайпа> и ipfw table 2 add <ip клиента> <номер пайпа>

Если вы хотите редиректить заблокированных пользователей, то необходимо будет добавить редирект на
локальный веб-сервер (например nginx)
${fwcmd} add 38 fwd 127.0.0.1,3128 tcp $LAN_NET to not me dst-port 80
${fwcmd} add 39 allow tcp from any $LAN_NET src-port 80

${fwcmd} add 40 deny all from $LAN_NET to not me
${fwcmd} add 41 deny all from not me to $LAN_NET , блокируем пакеты к нашим сетям. Под эти правила попадут те пользователи которые отстутствут в таблице 2 и таблице 3

${fwcmd} add 50 allow ip from me to any keep-state , разрешаем полный доступ исходящим соединениям с этого сервера и сохраняем их состояние.

${fwcmd} add 51 allow tcp from $IBGP_NET to $WAN_IP dst-port 179 , разрешаем хостам находяшимся в vlan со внутренним BGP устанавливать с нами соединение на 179 порт (bgp протокол)

${fwcmd} add 52 allow ospf from 195.xxx.xxx.192/29 to any , разрешаем ospf с определенной сети
Теперь мы задаем пайпы дамминета, собственно сам шейпинг
Примерно вот так:
#Speed 15Mbps, Для игр
${fwcmd} pipe 1 config mask dst-ip 0xffffffff bw 16000Kbit/s
${fwcmd} pipe 101 config mask src-ip 0xffffffff bw 16000Kbit/s

#Speed 20Mbps, Для отдыха
${fwcmd} pipe 2 config mask dst-ip 0xffffffff bw 21000Kbit/s
${fwcmd} pipe 102 config mask src-ip 0xffffffff bw 21000Kbit/s

#Speed 3Mbps, Для новичков
${fwcmd} pipe 3 config mask dst-ip 0xffffffff bw 3500Kbit/s
${fwcmd} pipe 103 config mask src-ip 0xffffffff bw 3500Kbit/s

В любом случае правим все под себя (не забыва что пакет сначала попадет под действие ipfw, а только потом перейдет на обработку pf) в соответствии с данными рекомендациями, на этом файрвол считаем настроенным. В принципе если вы не хотите использовать tablearg, то можно и не использовать. Есть еще варианты по pipe на клиента или по отдельной таблице на тариф. Эти варианты я не рассматриваю, в связи с высокой нагрузкой на оборудование (pipe на клиента) и неудобством (таблица на тариф)

Для полноты картины осталось только настроить NAT, который мы будем делать через pf.
Создаем файл /etc/pf.conf и вписываем в него следующие строки

WAN_IF=«em0», это наш внешний интерфейс в влане со внутренним bgp. Именно на нем мы будем производить NAT

LOCAL_NET=«10.249.0.0/16», это диапазон нашей локальной сети который будем NATить

nat on $WAN_IF from $LOCAL_NET to! $LOCAL_NET -> ($WAN_IF) round-robin sticky-address, а это само правило для NATа из внутренней сети на вшеншний интерфейс
параметр round-robin означает использование адресов пула по кругу.
параметр sticky-address используется для гарантии назаначения всегда одного и того же адреса источника в адрес пула.

Теперь приступаем к настройке пакета quagga
От него нам нужна поддержка bgp и поддержка ospf в моем случае

Zebra.conf я публиковать не буду, в нем все просто и однотипно. Описываем интерфейсы и статические маршруты, вот вырезка из него:

interface em0
ip address 195.xx.xxx.231/xxx
description ibgp
!
interface em1
ip address 195.xxx.xxx.193/29
description ospf
!
! Static routes.
!
ip route 10.0.0.0/8 Null0 254
ip route 79.142.80.132/30 195.xxx.xxx.238
ip route 94.124.180.57/30 195.xxx.xxx.225


Приводим конфиг bgp примерно вот к такому виду. Т.е принимаем маршрут по умолчанию от пограничных маршрутизаторов и отдаем ему какие-либо специфичные маршруты

hostname gw2.xxx.ru
password rxxxx
enable password rxxxxx
log file /var/log/quagga/bgpd.log
!
router bgp 3333
bgp router-id 195.xxx.xxx.231
bgp log-neighbor-changes
network 195.xxx.xxx.192/29
neighbor 195.xxx.xxx.225 remote-as 3333
neighbor 195.xxx.xxx.225 description j4350-1
neighbor 195.xxx.xxx.225 next-hop-self
neighbor 195.xxx.xxx.238 remote-as 3333
neighbor 195.xxx.xxx.238 description j4350-2
neighbor 195.xxx.xxx.238 next-hop-self
!

Далее мы настраиваем протокол OSPF (используется в моем случае, так-как параллельно Cisco Catalyst 3560 установлен Dlink DGS-3612)

Hostname gw2.xxx.ru
password xxx
enable password xxxx
log file /var/log/quagga/ospfd.log
!
interface em1
!
router ospf
ospf router-id 195.xxx.xxx.193
network 195.xxx.xxx.192/29 area 0.0.0.0
default-information originate metric 100
!
line vty


Если у вас стоит коммутатор который умеет работать с BGP, то лучше отказаться от использования OSPF и использовать для этих целей протокол BGP. В таком случае в
bgpd.conf надо внести следующие строки

neighbor 195.xxx.xxx.195 remote-as 3333
neighbor 195.xxx.xxx.195 description sw-c3560-xxx.ru
neighbor 195.xxx.xxx.195 default-originate


Вроде все настроили, теперь главное не забыть небольшой тюнинг sysctl
Добавляем в существующий /etc/sysctl.conf следующие строки
net.inet.ip.forwarding=1 #разрешаем пересылку пакетов между интерфейсами
net.inet.ip.fw.one_pass=1 #разрешаем однопроходный режим ipfw
net.inet.icmp.bmcastecho=0 #не отвечаем на пинг на широковещательный адрес
net.inet.tcp.blackhole=2 #отбрасываем пакеты на закрытый порт (не слушаемый никем)
net.inet.udp.blackhole=1 # отбрасываем пакеты на закрытый порт (не слушаемый никем)
net.inet.ip.dummynet.io_fast=1 #новое поведение dummynet( шейпирование канала)
net.inet.icmp.drop_redirect=1 #дропаем icmp редиректы
net.inet.icmp.log_redirect=1 #пишем icmp редиректы в log, бывает полезно
net.inet.ip.redirect=0 # если 0, то нет реакции на ICMP REDIRECT пакеты
net.inet.ip.dummynet.expire=0 # в случае кратковременного прекращения трафика пользователя не
#выносим его из очереди
net.inet.ip.dummynet.hash_size=16384 # размер хэш-таблицы, используемой dummynet для
#хранения очередей. Увеличение этого значение ускоряет работу
#dummynet при большом #количестве очередей, естественно в
# обмен на оперативную память


На этом наш шейпер считаем законченным, я заранее опустил интеграцию его с биллинговой системой, так как у каждого свое и свои методы
Можно смело писать команду reboot и если все было верно настроено то можно приступать к настройке коммутатора 3-го уровня (l3)

А если мы еще хотим переадресовывать пользователей которым не разрешен доступ в интернет на определенную страницу с информацией о блокировке, то ставим веб сервер nginx и пишем ему в конфиг (nginx.conf) следующий текст

user nobody;
worker_processes 2;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 35;
server {
listen 3128;
server_name wkt_router;
charset windows-1251;
access_log /dev/null;
rewrite ^(.*) blocked.wktnet.ru/index.htm permanent;
}
}

Настройка коммутатора 3-го уровня

В моей сети используется Cisco WS-C3560G-24-TS-S, там все просто
Заходим в режим конфигурирования: conf t
Присваиваем IP адрес интерфейсу vlan-а внутри которого бегает ospf, (та-же сеть что на шейпере на интерфейсе em1)
И настраиваем ospf

router ospf 1
router-id 195.xxx.xxx.195
log-adjacency-changes
network 10.249.33.0 0.0.0.255 area 0.0.0.0
network 10.249.42.0 0.0.0.255 area 0.0.0.0
network 10.249.51.0 0.0.0.255 area 0.0.0.0
network 10.249.55.0 0.0.0.255 area 0.0.0.0


сохраняем конфиг командой wr и радуемся достигнутому результату

если вместо протокола ospf вы решили использовать bgp то в конфиг коммутатора необходимо добавить следующие строки

router bgp 3333
bgp log-neighbor-changes
network 10.249.33.0 255.255.255.0
redistribute ospf 1
neighbor 195.xxx.xxx.193 remote-as 3333
neighbor 195.xxx.xxx.193 description gw


Если у вас используется l3 коммутатор D-link, то на нем настраиваем OSPF следующими командами

enable ospf, включаем ospf на коммутаторе
config ospf router_id 195.xxx.xxx.196, указываем id роутера
config ospf ipif <интерфейс> area 0.0.0.0 state enable, включаем ospf на интерфейсе

и сохраняем конфиг командой save

Настройка резервного сервера

На соседнем (резервном шейпере) мы производим аналогичную настройку, за исключением конфига ospf и IP адресов
Отличие в том, что default-information originate мы задаем метрику 200, тогда он становится резервным. Хотя можем оставить метрику 100 и получить балансировку нагрузки между шейперами, но в своей сети я этот вариант не использую так как при такой настройке клиенты получат двойную скорость (по тарифной скорости на каждом шейпере).

Заключение

В итоге мы получаем систему с динамической маршрутизацией (в случае отказа одного из шейперов или пограничных маршрутизаторов мы автоматически начинаем использовать соседний, все маршруты сети прописанные на коммутаторах 3-го уровня автоматически появляются в таблице маршрутизации на наших шейперах). Если вас пугает мысль о простаивающем сервере, то можете поиграться с настройками, сменить ospf на bgp, поставить еще один L3 коммутатор и выпускать часть сети через него.

Схема получившегося узла:
image

Для примера приведу свой rc.firewall

cat /etc/rc.firewall
#!/bin/sh
#Flush all firewall rules
/sbin/ipfw -q flush
/sbin/ipfw -q pipe flush
#Setting firewall path and options for working with rules
fwcmd="/sbin/ipfw -q"
#variables
IBGP_NET="195.93.xxx.xxx/28" #iBGP network (vlan9)
WAN_IP="195.93.xxx.xxx" #Primary WAN ip address
LAN_IP="195.93.xxx.xxx" #Primary LAN ip address

###System rules
${fwcmd} add 10 allow ip from any to any via lo0 #do not filter loobpack
${fwcmd} add 11 allow ip from me to me #allow packets from this host to this host
${fwcmd} add 12 allow icmp from any to me #allow ICMP

#allow ssh connections
${fwcmd} add 20 allow tcp from table\(6\) to me dst-port 22 #Allow SSH connections (table 6)
${fwcmd} add 29 deny tcp from any to me dst-port 22

#allowing users and add his ip to shaping pipe
${fwcmd} add 30 pipe tablearg ip from any to table\(1\) out via em1
${fwcmd} add 31 pipe tablearg ip from table\(2\) to any in via em1

#Block spammerss
${fwcmd} add 34 deny ip from table\(3\) to any dst-port 25 #for auto block spammers

#allow connections to this networsk
${fwcmd} add 40 allow all from table\(7\) to any
${fwcmd} add 41 allow all from any to table\(7\)

#allow active users
${fwcmd} add 45 allow all from not me to table\(1\)
${fwcmd} add 46 allow all from table\(2\) to not me

#By default block users
${fwcmd} add 48 fwd 127.0.0.1,3128 tcp from table\(8\) to not me dst-port 80
${fwcmd} add 49 allow tcp from any to table\(8\) src-port 80
${fwcmd} add 50 deny all from table\(8\) to not me
${fwcmd} add 51 deny all from not me to table\(8\)

###Access rules
#allow outgoing connections
${fwcmd} add 60 allow ip from me to any keep-state #allow all ougoing packets and keep state

#Rules allowing SNMP
${fwcmd} add 61 allow udp from table\(6\) to $WAN_IP dst-port 161 #Allow SNMP

#Rules allowing bgp
${fwcmd} add 64 allow tcp from $IBGP_NET to $WAN_IP dst-port 179 #Allow BGP from iBGP network (vlan 9)

###############################################заполняем таблицы
#таблица 6 - это те кто имеют доступ к маршрутизатору по ssh,telnet и snmp
${fwcmd} table 6 add 195.93.xxx.xxx #wkt office
${fwcmd} table 6 add 89.xxx.xxx.1 #
${fwcmd} table 6 add 93.xx.xxx.xxx #

#таблица 7 - IP адреса и сети с доступом при выключенном интернете
${fwcmd} table 7 add 195.xxx.xx.0/25 #binat
${fwcmd} table 7 add 195.xx.xx.0/26 #servers dmz
${fwcmd} table 7 add 195.xx3.xx.192/29 #int net
${fwcmd} table 7 add 195.xx.xxx.xxx #c3560-b51
${fwcmd} table 7 add 10.88.88.1
#таблица 8 - это наши локальные сети внутри которых пользователи
${fwcmd} table 8 add 10.87.0.0/16
${fwcmd} table 8 add 10.88.0.0/16
${fwcmd} table 8 add 10.249.0.0/16
${fwcmd} table 8 add 10.90.0.0/16
${fwcmd} table 8 add 195.93.xxx.0/25
${fwcmd} table 8 add 195.93.xxx.128/25
${fwcmd} table 8 add 195.93.xxx.64/26
${fwcmd} table 8 add 195.93.xxx.200/28

#таблицы 1 и 2 - это пользователи и их тариф
##WKT tech
${fwcmd} table 1 add 10.87.xx.250/32 55
${fwcmd} table 2 add 10.87.xx.250/32 255
${fwcmd} table 1 add 10.87.xx.251/32 55
${fwcmd} table 2 add 10.87.xx.251/32 255


И bgpd.conf

hostname gw2.xxxx
password rxxxx
enable password xxx
log file /var/log/quagga/bgpd.log
!
router bgp 44xxx
bgp router-id 195.93.xxx.xxx
bgp log-neighbor-changes
network 195.93.2xx.xxx/29
neighbor 195.93.206.xx remote-as 44380
neighbor 195.93.206.xx description j4350-b51
neighbor 195.93.206.xx next-hop-self
neighbor 195.93.206.xx remote-as 44380
neighbor 195.93.206.xx description j4350-k18
neighbor 195.93.206.xx next-hop-self
neighbor 195.93.206.xx remote-as 65000
neighbor 195.93.206.xxdefault-originate
neighbor 195.93.206.xx description sw-c3560g-24ts-b51
neighbor 195.93.206.xx route-map c3560g-b51-in in
neighbor 195.93.206.xx remote-as 65001
neighbor 195.93.206.xx default-originate
neighbor 195.93.206.xx description sw-c3560g-24ts-k18
neighbor 195.93.206.xx route-map c3560g-k18-in in
!
route-map c3560g-k18-in permit 10
set local-preference 200
!
route-map c3560g-b51-in permit 10
set local-preference 300


UPD: для того чтобы прописать в /boot/loader.conf kern.hz=«2000» не обязательно пересобирать ядро.
Tags:
Hubs:
+29
Comments 78
Comments Comments 78

Articles