Роутер на FreeBSD. Два канала связи. Основной + резервный.

Jul 13, 2010 11:59

В результате поисков - была обнаружена статейка "2 канала на ОС FreeBSD". Но в процессе реализации там были обнаружены некоторые опечатки и недоработки :)
В результате все было доведено до логического завершения. Решение предлагается далее.

Реализовывалось все это на FreeBSD 7.1-RELEASE.

Далее требуется установленный perl. (Подсказочка - установите просто Midnight Commander если у Вас его еще нет ;) При его инсталляции perl установится по зависимостям).

Далее нам потребуется PF. Но устанавливать его дополнительно, или пересобирать ядро вовсе нет необходимости. PF был интегрирован в основную систему, начиная с FreeBSD 5.3 в ноябре 2004. Единственный минус, что без пересборки ядра Вам не будут доступны ALTQ в PF. ALTQ (Alternate Queuing) предоставляет управление пропускной способностью Quality of Service (QoS), позволяющее гарантировать пропускную способность для различных сервисов на основе фильтрующих правил.

Включаем PF в файле /etc/rc.conf (Вносим и сохраняем).
gateway_enable="YES" # Включить поддержку работы в режиме шлюза
pf_enable="YES" # Включить PF (загрузить модуль если необходимо)
pf_rules="/etc/pf.conf" # определение правил для pf
pf_flags="" # дополнительные флаги для запуска pfctl
pflog_enable="YES" # запустить pflogd(8)
pflog_logfile="/var/log/pflog" # где pflogd должен сохранять протокол
pflog_flags="" # дополнительные флаги для запуска pflogd

Далее создаем конфигурационный файл /etc/pf.conf для PF, где прописываем все правила и фильтры.
# Main Setting
ext_if_1="vr0" # IPS_1 - интерфейс первого канала
ext_if_2="vr1" # IPS_2 - интерфейс второго канал
int_if="rl0" # lan - интерфейс внутренний сети
lo="lo0" # loopback

int_net="192.168.254.0/25" # LAN NETWORK
ext_addr_1="10.199.88.111" # IPS_1 wan
ext_addr_2="10.233.3.46" # IPS_2 wan
int_addr="192.168.254.2" # LAN IP

gw_1="10.199.88.110" # IPS_1 gw
gw_2="10.233.3.45" # IPS_2 gw

# services
tcp_svc="ssh"

#log - логирование пакетов
set loginterface $ext_if_1
set loginterface $ext_if_2
set loginterface $int_if

# skip iface - не фильтровать loopback,
# многие сервисы используют данный интерфейс под системные вещи.
set skip on lo0

# scrub
scrub in all

# NAT
# Натируем внешние интерфейсы
nat on $ext_if_1 inet from $int_net -> ($ext_if_1:0)
nat on $ext_if_2 inet from $int_net -> ($ext_if_2:0)

# INCOMING ROUTE
# маршрутизирем весь входящий трафик, под условием, если пришел на тот то интерфейс,
# то отправить необходимо ответ с того-то шлюза
# плюс проставляем теги. Теги помогут нам корректно пробрасывать порты,
# допустим у нас есть терминал сервер, пробросить можно следующим образом
rdr on $ext_if_2 proto tcp from any to $ext_addr_2 port 3389 tag EXT_IF_B -> 192.168.254.17 port 3389
rdr on $ext_if_1 proto tcp from any to $ext_addr_1 port 3389 tag EXT_IF_A -> 192.168.254.17 port 3389

# BLOCK ALL
# первоночально необходимо заблокировать весь входящий трафик
block in

# antispoof
antispoof quick for $int_if

# ICMP
# разрешаем icmp на внешних интерфейсах и маршрутизирем их по свои шлюзам
# чтобы не возникло ситуации пингуем один внешний адрес, а ответ идет по второму шлюзу.
# IPS_1
pass in on $ext_if_1 reply-to ($ext_if_1 $gw_1) inet proto icmp to ($ext_if_1) tag EXT_IF_A icmp-type echoreq code 0
pass in on $ext_if_1 inet proto icmp from ($ext_if_1:network) to ($ext_if_1) icmp-type echoreq code 0
# IPS_2
pass in on $ext_if_2 reply-to ($ext_if_2 $gw_2) inet proto icmp to ($ext_if_2) tag EXT_IF_B icmp-type echoreq code 0
pass in on $ext_if_2 inet proto icmp from ($ext_if_2:network) to ($ext_if_2) icmp-type echoreq code 0

# allow tcp ports
# разрешаем на внешних интерфейсах сервисы и маршрутизирем их, выше у нас разрешен только ssh
# для udp аналогичная запись за изменением только proto tcp на proto udp
# IPS_1
pass in on $ext_if_1 reply-to ($ext_if_1 $gw_1) inet proto tcp to ($ext_if_1) port { $tcp_svc }
pass in on $ext_if_1 inet proto tcp from ($ext_if_1:network) to ($ext_if_1) port { $tcp_svc }
# IPS_2
pass in on $ext_if_2 reply-to ($ext_if_2 $gw_2) inet proto tcp to ($ext_if_2) port { $tcp_svc }
pass in on $ext_if_2 inet proto tcp from ($ext_if_2:network) to ($ext_if_2) port { $tcp_svc }
# IPS_1
pass in quick from ($ext_if_1:network) tagged EXT_IF_A keep state
pass in quick reply-to ($ext_if_1 $gw_1) tagged EXT_IF_A keep state
# IPS_2
pass in quick from ($ext_if_2:network) tagged EXT_IF_B keep state
pass in quick reply-to ($ext_if_2 $gw_2) tagged EXT_IF_B keep state

# FIREWALL
# разрешаем все во внутреннем пространстве шлюза
pass out inet from (self:network)
pass in inet proto icmp to (self:network)

# LOCAL NETWORK
# Разрешаем весь трафик на выход из локальной сети
pass quick on $int_if

# OUTGOING ROUTE
# Маршрутизирем исходящий трафик
pass out route-to ($ext_if_1 $gw_1) inet from ($ext_if_1) keep state
pass out route-to ($ext_if_2 $gw_2) inet from ($ext_if_2) keep state
pass out inet from { $ext_if_1 $ext_if_2 } to (self:network)

Этого файла с правилами будет достаточно чтобы обеспечить «хождение трафика» по двум каналам, с правильной маршрутизацией.
В подробности впадать не буду, если захотите посмотреть, что означает каждое правило, то почитайте документацию к PF.

Далее, необходимо в /etc/rc.conf добавить основной шлюз:
defaultrouter="10.199.88.110"

Тот же самый IP, который прописывали как gw_1 в файле /etc/pf.conf

Cохраняем.

Далее нужно поставить пакетик для перла NET_PING:
cd /usr/ports/net/p5-Net-Ping
make install clean

Далее напишем собственно скрипт для переключения каналов в случае падение основного, и обратно в случае поднятия.
#!/usr/bin/perl -w
use strict;
use warnings;
use Net::Ping;
my $p = Net::Ping->new("icmp");
my $gw1 = "10.199.88.111"; # шлюз по умолчанию
my $gw2 = "10.233.3.46"; # резервный шлюз
my $now = localtime time;
my $log_file = "/var/log/change_route.log"; # путь и имя файла лог
my $command = `netstat -rn | grep default`;
my @b = split('\s+',$command,3);
if ($p->ping($gw1,0.05)){
print "host $gw1 is ok\n";
if($b[1] ne $gw1){
if($b[1] eq ""){
`route add default $gw1`;
}else{
`route change default $gw1`;
open(LOG,">>$log_file");
print LOG "$now Route change to $gw1\n";
close(LOG);
}
}
}else{
print "host $gw1 not responding .\n";
if($b[1] ne $gw2){
`route change default $gw2`;
open(LOG,">>$log_file");
print LOG "$now Route change to $gw2\n";
close(LOG);
}
}
$p->close();#

Сохраняем скрипт, делаем его исполняемым
chmod +x имя_файла_вашего_скрипта.pl

И закидываем в крон на исполнение раз в минуту. Т.е. добавляем в файл /etc/crontab строку
* * * * * root perl /путь_к_файлу/имя_вашего_скрипта.pl

Все. Перегружаем сервер! И если сделали все верно и правильно, то у Вас все уже должно работать ;)

Оригинал: http://www.ruip.ru/2009/08/28/router-na-freebsd-dva-kanala-svyazi-osnovnoy-rezervnyiy/

2 канала связи, pf, freebsd

Previous post Next post
Up