I. Списки

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

Синтаксис: {элементы_списка}

Замечание: запятая при перечислении элементов необязательна, достаточно пробела.

II. Макросы

Макросы — определяемые пользователем переменные, которые могут представлять собой IP адреса, номера портов, имена интерфейсов, и т.д.

      Имена макросов должны начаться с символа и могут содержать символы,цифры, и символы подчеркивания. Названия макросов не могут носить имена зарезервированных слов, типа pass, out, или queue.

Синтаксис:  <имя_макроса>="<значение>" # определение макроса
            $<имя_макроса>             # вызов макроса

Замечание:

           <значением> могут быть также списки и другие макросы, возможна рекурсия:
           <имя_макроса_списка>="{" <имя_макроса1><имя_макроса2> "}"

 

III. Таблицы

      Таблицы используются для хранения группы адресов IPv6 и/или IPv4.

Синтаксис:  table <имя_таблицы> { список_адресов }
            table <имя_таблицы> file <имя_файла>

Замечание: Поиски в таблице занимают гораздо меньше времени и потребляют меньше ресурсов, чем списки. По этой причине, таблица идеальна чтобы хранить большую группу адресов.

При определении таблиц могут использоваться следующие атрибуты:

const — содержание таблицы не может быть изменено после ее создания. Без этого атрибута pfctl ( 8 ) может использоваться для добавления или удаления элементов таблицы в любое время, при выполнении с securelevel (7), равным двум и выше.

persist — заставляет ядро сохранять таблицу в памяти, даже когда никакие правила к ней не обращаются. Без этого атрибута, ядро автоматически удалит таблицу, когда последнее правило, ссылающееся на нее будет отработано.

Управление таблицами также может осуществляться на лету, используя pfctl ( 8 ).


Синтаксис:
   pfctl -t <имя_таблицы> -T add    # добавить в таблицу
   pfctl -t <имя_таблицы> -T delete # удалить
   pfctl -t <имя_таблицы> -T show   # вывести на экран ip адреса из таблицы Это
                                    # также откроет таблицу, если она не существует.

IV. Правила фильтрации

Синтаксис: {pass,block}{in,out}[log][quick]on interface[proto{tcp,udp,...}]from SRC_IP[port SRC_PORT] to DST_IP[port DST_PORT][tcp_flags][state]

pass — разрешить
block — запретить
in/out — направление
log — указатель необходимости регистрации пакета через pflog ( 8 ) (регистрируется лишь первый пакет соединения, в противном случае log all)
quick — указатель на то, что список правил далее не должен просматривается и к пакету применяется данное правило
interface — интерфейс
proto — протокол
SRC_IP — источник
SRC_PORT — порт источника
DST_IP — адресат
DST_PORT — порт адресата
tcp_flags — Определяет флаги, которые должны быть установлены в заголовке TCP при использовании proto tcp. Флаги определяются как flags check/mask.
state — Определяет, сохраняется ли информация о состоянии на пакетах, соответствующих этому правилу.

варианты:
keep state — работает с TCP, UDP, и ICMP
modulate state — работы только с TCP. PF генерирует Initial Sequence Numbers (ISNs) для пакетов, соответствующих этому правилу.
synproxy state — проксирует входящие TCP соединения. Эта опция включает функциональные возможности keep state и modulate state.

V. Инициализация управления очередями ALTQ

      Поддержка ALTQ может быть включена только путем компилирования ядра FreeBSD с соответствующими параметрами.

Замечание: ALTQ поддерживается не всеми существующими драйверами сетевых карт. Для просмотра списка поддерживаемых устройств в вашем релизе FreeBSD обратитесь к стравеб сайта PF для FreeBSDнице справочника altq (4).

Следующие параметры включат ALTQ и добавят дополнительную функциональность:

options         ALTQ
options         ALTQ_CBQ        # Class Bases Queuing (CBQ)
options         ALTQ_RED        # Random Early Detection (RED)
options         ALTQ_RIO        # RED In/Out
options         ALTQ_HFSC       # Hierarchical Packet Scheduler (HFSC)
options         ALTQ_PRIQ       # Priority Queuing (PRIQ)
options         ALTQ_NOPCC      # Required for SMP build

options ALTQ — включает подсистему ALTQ
options ALTQ_CBQ — включает Class Based Queuing (CBQ). CBQ позволяет распределять пропускную способность соединений по классам или очередям для выставления приоритетов трафика на основе правил фильтрации.
options ALTQ_RED — включает Random Early Detection ( RED ). RED используется для предотвращения перегрузки сети. RED вычисляет длину очереди и сравнивает ее с минимальной и максимальной границей очереди. Если очередь превышает максимум, все новые пакеты отбрасываются. В соответствии со своим названием, RED отбрасывает пакеты из различные соединений в произвольном порядке.
options ALTQ_RIO — включает Random Early Detection In and Out.
options ALTQ_HFSC — включает Hierarchical Fair Service Curve Packet Scheduler. (Дополнительная информация http://www-2.cs.cmu.edu/~hzhang/HFSC/main.html)
options ALTQ_PRIQ — включает Priority Queuing ( PRIQ ). PRIQ всегда пропускает трафик из более высокой очереди первым.
options ALTQ_NOPCC — включает поддержку SMP для ALTQ . Эта опция необходима для SMP систем.

VI. Правила ALTQ

      Правила для очередей также задаются в конфигурационном файле (/etc/pf.conf)

Синтаксис: altq on interface scheduler bandwidth bw qlimit qlim tbrsize size queue {queue_list}

interface — сетевой интерфейс, на котором активируется очередь.
scheduler — какой планировщик будет использован. Доступные значения cbq и priq. На интерфейсе в один момент времени можно установить только один планировщик.
bw — общая пропускная способность, доступная планировщику. Может содержать аббревиатуры b, Kb, Mb, Gb для обозначения bits, kilobits, megabits, и gigabits в секунду, конкретное значение или процент от общей пропускной способности.
qlim — максимальное число пакетов в очереди. Необязательный параметр. По умолчанию — 50
size — размер token bucket regulator в байтах. Если не определен, то устанавливается на основе ширины полосы пропускания.
queue_list — список дочерних очередей, открываемых из родительской очереди.

Очередь определяется следующим образом

Синтаксис: queue name [on interface] bandwidth bw [priority pri][qlimit qlim] scheduler (sched_options) {queue_list}

name — имя очереди. Эта запись должна быть идентична определенной в директиве altq опцией queue_list. Для cbq это также может быть запись имени очереди в предыдущей директиве queue параметре queue_list. Имя не должно быть длиннее 15 символов.
interface — сетевой интерфейс, на котором запущена очередь. Это значение опциональное и если не определено, то очередь будет работать применительно ко всем интерфейсам. bw — общая пропускная способность, доступная планировщику. Может содержать аббревиатуры b, Kb, Mb, Gb для обозначения bits, kilobits, megabits, и gigabits в секунду, конкретное значение или процент от общей пропускной способности. Указывается только при использовании планировщика cbq.
pri — приоритет очереди. Для cbq приоритет изменяется от 0 до 7, для priq диапазон от 0 до 15. Приоритет 0 считается самым низким. Если этот параметр не определен, ему назначается 1. qlim — максимальное число пакетов в очереди. Необязательный параметр. По умолчанию — 50 scheduler — используемый планировщик — cbq или priq. Должен быть таким же, как и родительская очередь.
sched_options — дополнительные опции для управления планировщиком:
default — определить очередью по умолчанию, куда будут включаться все пакеты не подходящие под остальные очереди. Может быть только одна.
red — включить Random Early Detection (RED) для этой очереди.
rio — включить RED с IN/OUT. В этом режиме RED поддерживает очереди различной длины и различные пороговые значения, по одному на каждый уровень IP Quality of Service.
ecn — включить Explicit Congestion Notification (ECN) для этой очереди. Ecn работает совместно с red.
borrow — эта очередь может заимствовать пропускную способность у других очередей. Может быть определено только при использовании cbq.

pfctl -d выключить пакет-фильтр

pfctl -e включить пакет-фильтр
pfctl -f /etc/pf.conf загрузить конфиг из /etc/pf.conf

pfctl -nf /etc/pf.conf проверить на ошибки /etc/pf.conf, но не загружать его
pfctl -R -f /etc/pf.conf загрузить только правила фитрации
pfctl -N -f /etc/pf.conf загрузить только правила NAT
pfctl -O -f /etc/pf.conf load загрузить только опции

pfctl -F all сбросить все
pfctl -F rules сбросить только правила фильтрации
pfctl -F queue сбросить только очереди
pfctl -F nat сбросить правила NAT
pfctl -F info сбросить всю статистику
pfctl -z clear очистить все счетчики

pfctl -s rules показать текущие правила фильтрации
pfctl -v -s rules show показать информацию по каждому правилу фильтрации
pfctl -vvsr показать текущие правила фильтрации с добавлением номера правила
pfctl -v -s nat показать текущие правила NAT отдельно по каждому правилу
pfctl -s nat -i xl1 показать правила NAT для интерфейса xl1
pfctl -s queue показать очереди
pfctl -s state показать текущее состояние таблиц
pfctl -s info показать статистику правил и состояние счетчиков
pfctl -s all показать все что можно

pfctl -t testtable -T show просмотр содержимого таблицы
pfctl -t testtable -T show -v более подробной статистики по каждому адресу
pfctl -t testtable -T add 192.168.1.1 добавление адресов в таблицу
pfctl -t testtable -T delete 192.168.1.1 удаление адресов из таблицы

Недавно столкнулся с проблемой, настройка двух каналов в интернет на ОС FreeBSD
Ничего абсолютно сложного не предполагалось, но все же пришлось не много почитать документацию.

Собственно задача:

1. создать шлюз с двумя выходами в интернет, один основной, другой резервный.
2. минимизировать участие человека в смене на бек канал.

Инструменты:

ОС FreeBSD 6.x, PF, perl


Решение:

FreeBSD был поставлен с минимальной установкой, единственное изменение, которое нужно сделать
это добавить в ядро модуль PF. Делается это все не сложно.

cp /usr/src/sys/i386/conf/GENERIC /usr/src/sys/i386/conf/PF
ee /usr/src/sys/i386/conf/PF

добавляем строчки:
# PF

device pf
device pflog
device pfsync

# ALTQ

options ALTQ
options ALTQ_CBQ
options ALTQ_RED
options ALTQ_RIO
options ALTQ_HFSC
options ALTQ_PRIQ
options ALTQ_NOPCC

Далее нужно сохранить и пересобрать ядро:

cd /usr/src
make buildkernel KERNCONF=PF
make installkernel KERNKONF=PF
reboot

После загрузки системы нужно подправить /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

сохраняем.

далее нужно прописать правила для PF:

ee /etc/pf.conf

# Main Setting

ext_if_1=«rl0» # IPS_1 — интерфейс первого канала
ext_if_2=«rl1» # IPS_2 — интерфейс второго канал
int_if=«ae0» # lan — интерфейс внутренний сети
lo=«lo0» # loopback

int_net=«172.21.0.0/16» # LAN NETWORK
ext_addr_1=«222.0.0.2» # IPS_1 wan
ext_addr_2=«223.0.0.2» # IPS_2 wan
int_addr=«172.21.0.1» # LAN IP

gw_1=«222.0.0.1» # IPS_1 gw
gw_2=«223.0.0.1» # IPS_2 gw

# services

tcp_svc=«ssh» — разрешенные порты на внешних интерфейсах.

#log — логирование пакетов

set loginterface ae0
set loginterface rl0
set loginterface rl1

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

set skip on lo0

# scrub
scrub in all

# NAT
# Натируем внешние интерфейсы

nat on $ext_if_1 inet from !(self) -> ($ext_if_1:0) # IPS_1 nat
nat on $ext_if_2 inet from !(self) -> ($ext_if_2:0) # IPS_2 nat

# 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 }

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

# 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.

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

ee /etc/rc.conf

defaultrouter=«222.0.0.1»

сохраняем.

Далее нужно поставить пакетик для перла NET_PING:

cd /usr/ports/net/p5-Net-Ping
make install

Далле пишем сам скрипт для переключения каналов в случае падение основного, и обратно в случае поднятия.

Сохраняем скрипт, делаем его исполняемым, и закидываем в крон на исполнение раз в минуту.

Все. Перезагружаемся и у Вас все должно работать.

#!/usr/bin/perl -w

use strict;
use warnings;
use Net::Ping;

# 1 - автоматический режим переключение канала
# 2 - принудительное переключение на второй канал.

my $action = 1;
my $p = Net::Ping->new("icmp");
my $host_gw = "222.0.0.1"; # default gw
my $gw = "223.0.0.1";
my $now = localtime time;

if($action == 1){
my $command = `netstat -rn | grep default`;
my @b = split('s+',$command,3);
if ($p->ping($host_gw,0.05)){
print "host $host_gw is okn";
if($b[1] ne $host_gw){
if($b[1] eq ""){
`route add default 222.0.0.1`;
}else{
`route change default 222.0.0.1`;
open(LOG,">>/change_route.log");
print LOG "[!] $now Route change to 222.0.0.1n";
close(LOG);
}
}
}else{
print "host $host_gw is bad.n";
if($b[1] ne $gw){
`route change default 223.0.0.1`;
open(LOG,">>/change_route.log");
print LOG "[!] $now Route change to 223.0.0.1n";
close(LOG);
}
}
$p->close();
}

if($action == 2){
my $command = `netstat -rn | grep default`;
my @b = split('s+',$command,3);
if($b[1] ne $gw){
if($b[1] eq ""){
`route add default 223.0.0.1`;
}else{
`route change default 223.0.0.1`;
open(LOG,">>/change_route.log");
print LOG "[!] $now Route change to 223.0.0.1n";
close(LOG);
}
}
}

 

 

http://habrahabr.ru/post/66851/

Итак, о задаче: есть два канала интернет, шлюз на FreeBSD

gate# uname -a
FreeBSD gate 9.0-RELEASE FreeBSD 9.0-RELEASE #0: Thu Nov  1 06:48:52 OMST 2012     root@gate:/usr/obj/usr/src/sys/GATE  amd64

Не то, чтобы необходимость, но желание создать гибкую систему с балансировкой трафика по каналам и желание получить премию от руководства.
Канал №1: безлимитка, скорость 7 Мб, реальный ip-адрес
Канал №2: безлимитка, скорость до 60Мб, реальный ip-адрес.
Со стороны провайдера были установлены шлюзы, через которые реализую DMZ на «ловушки» для хакеров, поэтому настройки PF и SQUID минимальны

Описание поднятия балансировки

Все манипуляции от пользователя root. (Подключаемся к хосту любым пользователем, затем su, пароль root).

1. Опции ядра для включения PF:
Если не считать трафик средствами PF, то второй пункт можно отключить

cd /sys/amd64/conf
cp GENERIC GATE
ee GATE

device          pf
device          pflog
options ALTQ
options ALTQ_CBQ
options ALTQ_RED
options ALTQ_RIO
options ALTQ_HFSC
options ALTQ_PRIQ
options ALTQ_NOPCC

2. сборка ядра

make kernel KERNCONF=GATE

3. В шлюз установлены 2 сетевых карты, одна интегрированная в материнскую плату:

re0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=389b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_UCAST,WOL_MCAST,WOL_MAGIC>
        inet 192.168.1.2 netmask 0xffffff00 broadcast 192.168.1.255
re1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=389b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_UCAST,WOL_MCAST,WOL_MAGIC>
        inet 192.168.63.26 netmask 0xfffffff8 broadcast 192.168.63.31
nfe0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=82008<VLAN_MTU,WOL_MAGIC,LINKSTATE>
        inet 10.1.1.30 netmask 0xffffff00 broadcast 10.1.1.255 

re0 провайдер №1, re1 — провайдер №2. Внутренний интерфейс nfe0 (назначение ip адресов и масок сложилось исторически, я работаю с тем, что есть). И конечно же подняты vlan на внутреннем интерфейсе, раскидываю по ним систему ip видеонаблюдения и различные отделы компании.

4. Настройка rc.conf

ee /etc/rc.conf
pf_enable=«YES»
pf_rules=«/etc/pf.conf»
squid_enable=«YES»
reboot

То есть при загрузке выполняются правила из файла /etc/pf.conf.

5. Листинг pf.conf.

cat pf.conf
#Задаем переменные, где int_if - внутренний интерфейс, ext_if - внешние.
int_if=«nfe0»
ext_if=«re1»
ext_if2=«re0»
int_net=«10.1.1.0/24»
freeBSD=«10.1.1.30»
icmp_types=«{ echoreq, unreach}»
http=«80»
https=«443»
ssh=«22»

#описываем шлюзы для каждого из провайдеров.
gw1=«192.168.63.25»
gw2=«192.168.1.1»

#Для тех, кто должен идти с конкретного провайдера вводим принудительное направление траффика в обход балансировки.
#Например, клиент-банки или тендерные площадки чувствительны к смене ip-адреса при балансировке.
to_ertel=«{10.1.1.42, 10.1.1.33, 10.1.1.4, 10.1.1.12, 10.1.1.5, 10.1.1.48, 10.1.1.25, 10.1.1.3, 10.1.1.243, 10.1.1.5 }»
to_trans=«{ 10.1.1.27, 10.1.1.2, 10.1.1.181, 10.1.1.46, 10.1.1.39, 10.1.1.27, 10.1.1.31, 10.1.1.113 }»

#настройки политик файрвола - всех из вне посылать,
set block-policy drop
>set skip on lo
#собирать все пакеты перед их отправкой.
scrub in all

#Нат на первый интернет-вход
nat on $ext_if inet from any to any -> ($ext_if)
#Нат на второй интернет-вход
nat on $ext_if2 inet from any to any -> ($ext_if2)
#Проброс на SQUID
rdr on $int_if inet proto tcp from any to any port www -> 127.0.0.1 port 3128

block in from any to any
block out from any to any

#Антиспуфинг
antispoof quick for $int_if inet

#  пропускаем все исходящие пакеты на внутреннем интерфейсе
pass out on $int_if from any to $int_net

#проброс без балансировки (согласно списку адресов)
pass in quick on $int_if route-to ($ext_if2 $gw2) from $to_trans to !$int_net keep state
pass in quick on $int_if route-to ($ext_if $gw1) from $to_ertel to !$int_net keep state

#  пропускаем (quick) пакеты предназначенные самому шлюзу
pass in quick on $int_if from $int_net to $int_if
pass in quick on $int_if route-to { ($ext_if $gw1), ($ext_if2 $gw2)} round-robin sticky-address proto tcp from $int_net to any flags S/SA keep state
#Добавил sticky-address по дельному совету RicoX

#  балансировка исходящего icmp и udp трафика идущего из внутренней сети
pass in on $int_if route-to { ($ext_if $gw1), ($ext_if2 $gw2) } round-robin proto { udp, icmp } from $int_net to any keep state

#  основные "выпускаюшие" правила на внешнем интерфейсе
pass out on $ext_if proto tcp from any to any flags S/SA modulate state
pass out on $ext_if proto { udp, icmp } from any to any keep state
pass out on $ext_if2 proto tcp from any to any flags S/SA modulate state
pass out on $ext_if2 proto { udp, icmp } from any to any keep state
#  маршрутизация пакетов идущих с любого IP на $ext_if1 через $ext_gw1 и
#  пакетов идущих  на $ext_if2 через $ext_gw2
pass out on $ext_if route-to ($ext_if2 $gw2) from $ext_if2 to any
pass out on $ext_if2 route-to ($ext_if $gw1) from $ext_if to any

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

Недостаток данного пути в том, что иногда, когда что-то случается со стороны одного из провайдеров, балансировка нагрузки работать перестает.
Для решения данной проблемы были созданы конфигурационные файлы: pf.conf.ertelecom и pf.conf.trans, которые отличаются от основного файла настроек только тем, что строка:

 #pass in on $int_if route-to { ($ext_if $gw1), ($ext_if2 $gw2) } round-robin proto { udp, icmp } from $int_net to any keep state 

из них выкинута. Вместо нее появляются две строки:

pass out route-to ($ext_if $gw1) from $int_if to any
pass out route-to ($ext_if2 $gw2) from $int_if to any

одна из которых работает, в зависимости от того, какой шлюз по умолчанию установлен.

Скрипты переключения выглядит так:

cat er_conn.sh 
#!/bin/sh

GW1=«192.168.63.25»
if1=«192.168.63.26»
#отключаем файрволл
/sbin/pfctl -d
#и включаем файрволл с правилами провайдера №2
/sbin/pfctl -e -f /etc/pf.conf.dom
#Убиваем шлюз по-умолчанию
/sbin/route del default
#Включаем шлюз по-умолчанию нашего провайдера №2
/sbin/route add default $GW1

Аналогично этому скрипту работают скрипты управления включением провайдера №1 и балансировки нагрузки. Меняется только адрес шлюза и файл правил настройки pf.
В cron подключаем скрипт, который каждые несколько (на выбор) минут проверяет на ответ от шлюза:
ping -S <ip адрес шлюза провайдера> <наш реальный ip-адрес>.
Если ответа нет, автоматически подгружаем правила живого канала.

Что касается настройки SQUID, то там я сделал вообще все примитивно. Собрал squid 3 из портов, не меняя ничего, кроме настройки transparent, то есть прозрачный прокси. Привожу его конфигурационный файл:

cat squid.conf
http_port 127.0.0.1:3128 transparent
icp_port 0
hierarchy_stoplist cgi-bin ?
acl QUERY urlpath_regex cgi-bin ?
no_cache deny QUERY
cache_mem 256 MB
maximum_object_size 8092 KB
maximum_object_size_in_memory 512 KB
cache_dir ufs /bkp/var/squid/cache 2048 64 256
cache_access_log /bkp/var/squid/access.log
cache_log /bkp/var/squid/cache.log
cache_store_log /bkp/var/squid/store.log
cache_mgr root@xxx.ru
cache_effective_user squid
cache_effective_group squid
visible_hostname gate
coredump_dir /bkp/var/squid/cache
pid_filename /var/run/squid/squid.pid
acl our_networks src 10.1.1.0/24
http_access allow our_networks

Вот с этим и работаем уже около двух лет, практически ничего не вылетает, шлюз перезагружается только по прихотям электриков, пользователи забыли, что такое проблемы с доступом интернет.
Трафик считаю спиртом (cnupm), с последующей выгрузкой его логов в базу MySQL.

Небольшой бонус: когда я принял хозяйство, все IP были выставлены вручную для машин. Кроме того, к этим IP (а не именам машин) была привязка некоторых сервисов локальной сети. Поэтому родился следующий скрипт для быстрого трансфера настроек IP в формат DHCP конфигурационного файла сервера, а именно той части, которая присваивает IP по МАК-адресу.
Привожу листинг:

  gate# cat do_dhcp 
#!/bin/sh

arp -an | grep <Ваш локальный сетевой интерфейс> > /usr/arp.txt
#В IFS указан пробел в кавычках
IFS=« »
cat /usr/arp.txt | while read line
do
    set -- $line;
    echo -e «host $1 {
                    hardware ethernet $4;
                    fixed-address $2;
                }n»
done

Вывод происходит на экран терминала, но если в echo добавить после последней кавычки > /usr/dhcp.txt, то вывод будет в файл. Далее произошел copy-paste вывода в соответствующий раздел файла dhcpd.conf. Можно так:

cat dhcpd.txt >> /usr/local/etc/dhcpd.conf

http://habrahabr.ru/post/177767/

* «sysctl -w net.inet.tcp.msl=7500» — время ожидания ACK в ответ на SYN-ACK или
FIN-ACK в миллисекундах;
* «sysctl -w net.inet.tcp.blackhole=2» — все пакеты на закрытый порт
отбрасываются без отсылки RST;
* «sysctl -w net.inet.udp.blackhole=1» — отбрасывать пакеты для закрытых портов;
* «sysctl -w net.inet.icmp.icmplim=50» — защита от генерирование потока ответных пакетов, 
максимальное количество ICMP Unreachable и TCP RST пакетов в секунду;
* «sysctl -w kern.ipc.somaxconn=32768» — увеличение числа одновременно открытых сокетов;
* Сборка ядра с опцией DEVICE_POLLING (далее: sysctl kern.polling.enable=1;
sysctl kern.polling.user_frac=50);

Проблема приоритизации трафика, на мой взгляд, весьма актуальна. 
Интернет-канала много не бывает и на всех пользователей и сервисов локальной зачастую не хватает. Поэтому для нормальной работы Интернета требуется грамотное распределения полосы с учетом потребностей каждого из участников. 
Единственный раз, когда мне не понадобился QoS — это гарантированный провайдером канал в 20 Мбит/с в мир и 100Мбит/с — национальный. Но такое удовольствие не из дешевых, поэтому зачастую народ довольствуется ADSL-каналом с заявленной скоростью к клиенту до 5-10 Мбит/с.

Хочу заметить, что данная статья не предназначенная для новичков в сетевом администрировании в целом и в PF в частности. Читателю необходимо иметь минимальные навыки работы с сетями (понимать устройство пакета, знать что такое ТСР-флаги и т.д.), а также с пакетным фильтром PF. Не лишним будет прочесть официальный FAQ и man'ы. В основном цель этой статьи поделиться опытом, выслушать замечания и, возможно, улучшить свой вариант.

И так, постановка задачи: организовать доступ к сети Интернет. Грамотно распределить как входящий так и исходящий трафик с разделением канала для клиентов локальной сети и сервисов, запущенных на самом роутере (например, FTP-сервер, SIP-сервер и т.д.). В качестве роутера выступает сервер с ОС FreeBSD 9 c пакетным фильтром PF.
Протокол FTP будет использоваться только в пассивном режиме, что немного упростит конфигурацию.

Для решения поставленной задачи необходимо пересобрать ядро и включить в него поддержку PF и ALTQ. Для задач, не требующих ALTQ, пересобирать ядро не обязательно. Можно просто подгрузить PF как модуль.

Добавляем в файл конфигурации следующие строки и пересобираем ядро. Описывать каждую опцию не буду. В man 4 altq все есть.

options HZ=<span class="number" style="color: #2aa198;">1000</span>
device pf
device pflog
options ALTQ
options ALTQ_CBQ
options ALTQ_RED
options ALTQ_RIO
options ALTQ_HFSC
options ALTQ_CDNR
options ALTQ_PRIQ
#options ALTQ_NOPCC #<span class="keyword" style="color: #859900;">for</span> SMP CPU



Лично я для боевого сервера пересобираю не только ядро системы, но и мир. Как это сделать, хорошо описано в Хендбуке и /usr/src/Makefile.

Для автоматического запуска PF при старте системы добавляем в /etc/rc.conf строки:

<span class="setting">pf_enable=<span class="value"><span class="string" style="color: #2aa198;">"YES"</span></span></span>
<span class="setting">pflog_enable=<span class="value"><span class="string" style="color: #2aa198;">"YES"</span></span></span>



Далее, собственно, сам конфигурационный файл пакетного фильтра. Возьмем самую простую реализацию роутера: один внутренний сетевой интерфейс и один внешний. Интернет канал подключен через ADSL-модем, работающий в режиме моста, т.е. подключение pppoe организовано средствами штатного ppp-клиента. 
Скорость от провайдера - 5 Мбит/c, к провайдеру - 850 Кбит/c. На роутере запущен HTTP-прокси для прозрачного перенаправления WWW-трафика пользователей сети. Это сделано с целью блокировать метод CONNECT и принудительно направить другие виды трафика (например, торрент) в другие очереди с другим приоритетом. Я использую легковесный, но «шустрый» 3proxy (3proxy.ru). Кому важен кэш - используйте Squid или Apache Traffic Server.

Также перенаправляются все ДНС-запросы на сервер ДНС-провайдера. Это сделано с целью блокировки фишинговых и других зловредных сайтов, сайтов для взрослых, аплоадеры, соц. сети и т.д. и т.п. Некоторые компании так же предоставляет блокировку рекламных баннеров.

Весь исходящий трафик я разбил на следующие очереди:

— ДНС-запросы — очередь u_dns
— ТСР АСК-пакеты — очередь u_ack
— трафик с высоким приоритетом — очередь u_hipri
— трафик с нормальным приоритетом — очередь u_pri
— трафик с низким приоритетом — очередь u_lowpri
— весь остальной трафик — очередь u_other
— стандартная очередь — очередь u_std, 

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

Аналогичное деление трафика, идущего к клиенту локальной сети, только вместо u_* используется d_* обозначение.

Трафик классифицируется как относительно сервиса/протокола (HTTP, FTP, Torrent) так и относительно пользователя, т.е. трафик от/к компьютера, например, бухгалтера может быть весь помечен как с высоким приоритетом, независимо от того Торрент это или FTP.
Данные, являющиеся ДНС-запросам и ТСР АСК-пакетами, для всех пользователей имеют свой высокий неизменный приоритет. Например, весь трафик от/к компьютера, относящегося к группе с низким приоритетом, будет обрабатываться с низким приоритетом, кроме ДНС и ТСР АСК.

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

<span class="setting">mst=<span class="value"><span class="string" style="color: #2aa198;">"modulate state"</span></span></span>
<span class="setting">str=<span class="value"><span class="string" style="color: #2aa198;">"source-track rule"</span></span></span>
<span class="setting">ext_if=<span class="value"><span class="string" style="color: #2aa198;">"tun0"</span></span></span>
<span class="setting">int_if=<span class="value"><span class="string" style="color: #2aa198;">"rl0"</span></span></span>



Таблица, в которую включены компьютеры трафик к/от которых весь будет считаться с высоким приоритетом.

table <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">pc_hipri</span>></span> persist {10.11.1.2}



Аналогично для ПК с нормальным приоритетом.

table <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">pc_pri</span>></span> persist {10.13.1.2 10.13.1.10 10.13.1.13 10.13.1.14 10.13.1.15}



Таблица с адресами, доступ к которым блокируется.

table <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">ban</span>></span> persist file "/etc/pf.ban"



Таблица, с адресами клиентов, которым можно доверять.

table <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">trust</span>></span> persist {123.10.456.0/24 193.196.125.0/24}



IP-адрес системного администратора.

table <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">me</span>></span> persist {210.211.13.84}



IP-адреса SIP-провайдеров

table <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">sip_peers</span>></span> persist {212.15.65.122 75.16.127.118}



Конфигурируем опции пакетного фильтра, изменяющие его поведение. В нашем случае:

— не производится проверка на интерфейсе обратной петли;
— выставляем оптимизацию правил в basic;
— устанавливаем привязку состояний соединения (т.н. стейтов) к каждому интерфейсу;
— устанавливаем максимальное кол-во состояний. 

Если сетевых интерфейсов большое кол-во или большое кол-во клиентов в сети, возможно, потребуется скорректировать это значение в большую сторону.

<span class="operator"><span class="keyword" style="color: #859900;">set</span> skip <span class="keyword" style="color: #859900;">on</span> lo0
<span class="keyword" style="color: #859900;">set</span> ruleset-optimization basic
<span class="keyword" style="color: #859900;">set</span> state-policy if-bound
<span class="keyword" style="color: #859900;">set</span> limit states <span class="number" style="color: #2aa198;">20000</span></span>



Нормализация трафика (т.н. скрабинг). Срабинг позволяет значительно повысить безопасность файервола. В нашем случае выполняется нормализация трафика на внешнем интерфейсе, генерируется случайная последовательность в поле идентификации IP-пакета, устанавливается величина TTL=128, производится «дефрагментация» IP-пакетов, а также нормализация TCP соединений.

scrub on <span class="variable" style="color: #b58900;">$ext_if</span> all random-id <span class="keyword" style="color: #859900;">no</span>-df min-ttl <span class="number" style="color: #2aa198;">128</span> fragment reassemble reassemble tcp




ALTQ.

Приоритизировать будем на всех физических сетевых интерфейсах. На внешнем — исходящий трафик, на внутреннем — входящий. Выбор дисциплины — очень важный момент. Возможные варианты: priq, cbq, hfsc. 

Я использую hfsc, т.к. важной чертой этого планировщика есть именно гарантирование полосы для конкретного пользователя/сервиса. Работа планировщика для нашего конкретного случая построена следующим образом: если в описании очереди указан дополнительный параметр realtime, то ей будет отдана указанная (realtime) полоса независимо ни от чего. Величина трафика свыше realtime будет рассчитываться исходя из величины bandwidth. Причем bandwidth — не абсолютный параметр, скорее относительный.

Исходя из величин bandwidth каждой очереди рассчитывается величина трафика свыше realtime для каждой очереди пока не будет достигнут параметр upperlimit, который жестко ограничивает полосу. Приоритеты в планировщике hfsc не используются. 

Помимо всех прочих параметров, hfsc имеет параметр qlimit — кол-во слотов, доступных очереди для сохранения исходящих пакетов, когда вся доступная полоса исчерпана. И только когда все слоты будут заняты, пакеты будут отбрасываться, что заставит клиента снижать скорость. Мы не будем использовать RED или ECN, вместо этого увеличим значение qlimit.

Назначаем очередь на внешнем интерфейсе. Величина bandwidth должна быть 96% от предоставляемой вышестоящим роутером. Здесь же перечисляем все дочерние очереди. 

altq on <span class="variable" style="color: #b58900;">$ext_if</span> hfsc bandwidth <span class="number" style="color: #2aa198;">800</span>Kb queue {u_std,u_ack,u_dns,u_hipri,u_pri,u_lowpri,u_other}



Очередь по-умолчанию.
Будет потреблять 25 кбит/с независимо ни от чего. Величина bandwidth 1Kb означает, что если канал будет полностью занят любой другой очередью или всеми, то очередь u_std практически ничего не получит свыше 25 кбит/с.

queue u_std bandwidth <span class="number" style="color: #2aa198;">1</span>Kb qlimit <span class="number" style="color: #2aa198;">50</span> hfsc (<span class="keyword" style="color: #859900;">default</span> realtime <span class="number" style="color: #2aa198;">25</span>Kb)



Очередь u_ack — это ТСР АСК-пакеты, которые будут отправляться удаленному хосту с которого происходит загрузка по протоколу ТСР. Важно, что бы эти пакеты проходили без задержек. Для максимальной скорости от провайдера 4 Мбит/с требуется гарантированный канал в обратную сторону в размере 125 кбит/с.

queue u_ack bandwidth 1Kb qlimit 200 hfsc (realtime 125Kb)



ДНС-запросы. 
Гарантированной полосы в 25 кбит/с вполне достаточно. Больше не нужно, поэтому bandwidth 1Kb

queue u_dns bandwidth 1Kb qlimit 50 hfsc (realtime 25Kb)



Очередь с высоким приоритетом

queue u_hipri bandwidth 300Kb qlimit 250 hfsc (realtime 200Kb)



Очередь с обычным приоритетом

queue u_pri bandwidth 300Kb qlimit 400 hfsc (realtime 150Kb)



Очередь с низким приоритетом

queue u_lowpri bandwidth 100Kb qlimit 100 hfsc (realtime 75Kb)



Очередь для всего остального трафика ТСР и UDP.

queue u_other bandwidth 97Kb qlimit 50 hfsc (realtime 25Kb)



Назначаем очереди на внутреннем интерфейсе — приоритизируем входящий трафик. 

Провайдер отдает 5 Мбит/с, поэтому устанавливаем очередь inetq в размере 96%. Так же на внутреннем интерфейсе запущен ряд служб, например, локальный FTP, поэтому важно «не смешать» локальный трафик с Интернет-трафиком. Так как сетевая карточка 100Mbit, то выставляем значение bandwidth в 100Mb. Назначаем две очереди: одна — локальный трафик, вторая Интернет-трафик с дочерними очередями.

altq on <span class="variable" style="color: #b58900;">$int_if</span> hfsc bandwidth <span class="number" style="color: #2aa198;">100</span>Mb queue {etherq, inetq}



Очередь для локального трафика.
В эту очередь будут попадать все пакеты, идущие от внутреннего сетевого интерфейса к пользователям локальной сети. Параметр upperlimit определяет максимальное значения для данной очереди. Заметьте, в эту очередь не будут попадать ответы, например, от WWW-сервера из сети Интернет. Эта очередь исключительно для локального трафика.

queue etherq bandwidth 95Mb hfsc (upperlimit 95Mb)



Очередь для Интернет-трафика. 
В эту очередь будут попадать пакеты, идущие с сети Интернет. Имеет дочерние очереди по аналогии с внешним интерфейсом.

queue inetq bandwidth <span class="number" style="color: #2aa198;">4800</span>Kb hfsc (upperlimit <span class="number" style="color: #2aa198;">4800</span>Kb) {d_std,d_ack,d_dns,d_hipri,d_pri,d_lowpri,d_other}
queue d_std bandwidth <span class="number" style="color: #2aa198;">1</span>Kb qlimit <span class="number" style="color: #2aa198;">50</span> hfsc (<span class="keyword" style="color: #859900;">default</span> realtime <span class="number" style="color: #2aa198;">25</span>Kb)
queue d_ack bandwidth <span class="number" style="color: #2aa198;">1</span>Kb qlimit <span class="number" style="color: #2aa198;">50</span> hfsc (realtime <span class="number" style="color: #2aa198;">50</span>Kb)
queue d_dns bandwidth <span class="number" style="color: #2aa198;">1</span>Kb qlimit <span class="number" style="color: #2aa198;">50</span> hfsc (realtime <span class="number" style="color: #2aa198;">25</span>Kb)
queue d_hipri bandwidth <span class="number" style="color: #2aa198;">1297</span>Kb qlimit <span class="number" style="color: #2aa198;">500</span> hfsc (realtime <span class="number" style="color: #2aa198;">1000</span>Kb)
queue d_pri bandwidth <span class="number" style="color: #2aa198;">2000</span>Kb qlimit <span class="number" style="color: #2aa198;">500</span> hfsc (realtime <span class="number" style="color: #2aa198;">2000</span>Kb)
queue d_lowpri bandwidth <span class="number" style="color: #2aa198;">1000</span>Kb qlimit <span class="number" style="color: #2aa198;">500</span> hfsc (realtime <span class="number" style="color: #2aa198;">500</span>Kb)
queue d_other bandwidth <span class="number" style="color: #2aa198;">500</span>Kb qlimit <span class="number" style="color: #2aa198;">500</span> hfsc (realtime <span class="number" style="color: #2aa198;">240</span>Kb)



Правила трансляции локальных адресов (NAT).

Транслируются адреса, где источник — IP-адрес из любой подсети внутреннего интерфейса, а адрес назначения — любой, кроме IP-адресов из всех подсетей, подключенных к роутеру. Это могут быть как физические так и VPN-интерфейсы (tun, gif).
($ext_if) — в круглых скобках, т.к. IP-адрес внешнего интерфейса назначается динамически.
($int_if:network) и (self) в круглых скобках что бы в выводе pfctl -sn не было подстановки реальных адресов и сетей. Это удобно, когда у вас на внутреннем интерфейсе несколько алиасов и, соответственно, подсетей (как в моем случае).

nat on <span class="variable" style="color: #b58900;">$ext_if</span> inet from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to !(<span class="keyword" style="color: #859900;">self</span>) -> (<span class="variable" style="color: #b58900;">$ext_if</span>) port <span class="number" style="color: #2aa198;">1024</span>:<span class="number" style="color: #2aa198;">65535</span>



Пользователей из группы pc_hipri и pc_pri пускаем в обход прокси. Я пускаю их напрямую, т.к. эти пользователи не нуждаются в контроле, а также специфическое ПО не работает в режиме прозрачного проксирования.

no rdr on $int_if inet proto tcp from {<span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">pc_hipri</span>></span> <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">pc_pri</span>></span>} to !(self) port 80



Правило редиректа, перенаправляющие все ДНС-запросы локальных пользователей на
внешний ДНС-сервер. Это может быть Google или, лучше, ДНС-сервер компании,
предоставляющей фильтрацию трафика посредством ДНС.

rdr on <span class="variable" style="color: #b58900;">$int_if</span> inet proto {tcp udp} from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to !(<span class="keyword" style="color: #859900;">self</span>) port <span class="number" style="color: #2aa198;">53</span> -> <span class="number" style="color: #2aa198;">193.58</span>.<span class="number" style="color: #2aa198;">251.251</span> port <span class="number" style="color: #2aa198;">53</span>



Редирект на прокси-сервер.

rdr on <span class="variable" style="color: #b58900;">$int_if</span> inet proto tcp from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to !(<span class="keyword" style="color: #859900;">self</span>) port <span class="number" style="color: #2aa198;">80</span> -> <span class="number" style="color: #2aa198;">127.0</span>.<span class="number" style="color: #2aa198;">0.1</span> port <span class="number" style="color: #2aa198;">31280</span>



Редирект на удаленный рабочий стол виндовой машины в локальной сети из сети Интернет.

rdr on <span class="variable" style="color: #b58900;">$ext_if</span> inet proto tcp from any to (<span class="variable" style="color: #b58900;">$ext_if</span>) port <span class="number" style="color: #2aa198;">3389</span> -> <span class="number" style="color: #2aa198;">10.11</span>.<span class="number" style="color: #2aa198;">1.2</span> port <span class="number" style="color: #2aa198;">3389</span>



Правила фильтрации трафика. Правила будем группировать в такой последовательности: действие, интерфейс, направление, протокол, адрес источника, порт источника, адрес назначения, порт назначения.

Антиспуфинг.

antispoof quick <span class="keyword" style="color: #859900;">for</span> {<span class="variable" style="color: #b58900;">$int_if</span> lo0} inet



Блокируем не маршрутизируемые адреса.

block <span class="keyword" style="color: #859900;">in</span> quick inet from no-route to any



Блокируем броадкасты.

block <span class="keyword" style="color: #859900;">in</span> quick on $ext_if inet from any to <span class="number" style="color: #2aa198;">255.255</span>.<span class="number" style="color: #2aa198;">255.255</span>



Блокируем IP-адреса, содержащиеся в таблице ban. Опция return возвращает TCP RST, что закрывает сразу соединение без таймаута. Полезно, когда блокируются адреса рекламных сетей, что позволяет браузеру нормально загружать странички без ожидания загрузки блокируемого контента.

block return out quick on $ext_if inet from any to <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">ban</span>></span>



Эти 2 правила определяют тип файервола: запрещено все, кроме явно разрешенного.

block <span class="keyword" style="color: #859900;">in</span> all
block out all



Собственно разрешающие правила. 
Расположены в определенной последовательности, что важно с точки зрения производительности, а также ожидаемого эффекта.

Внутренний интерфейс.

Разрешаем ТСР-пакеты к интерфейсу обратной петли, на котором «слушает» прокси-сервер для организации прозрачного проксирования. Назначаем очереди. Мы не можем приоритизировать входящий трафик, только исходящий. В d_pri будут попадать пакеты, которые являются ответом от сервера к клиенту, таким образом мы регулируем скорость скачивания.

pass in <span class="keyword" style="color: #859900;">log</span> quick on <span class="variable" style="color: #b58900;">$int_if</span> inet proto tcp from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to <span class="number" style="color: #2aa198;">127.0</span>.<span class="number" style="color: #2aa198;">0</span>.<span class="number" style="color: #2aa198;">1</span> port <span class="number" style="color: #2aa198;">31280</span> queue (d_pri, d_ack)



Далее 3 правила, которые разрешают ТСР соединения на определенные порты на не локальные IP-адреса. Назначаются соответствующие очереди. Тегирование необходимо что бы потом на внешнем интерфейсе мы могли «отделить» его от остального трафика и направить в нужную очередь. Заметьте, опция quick не используется, поэтому эти правила нужно расположить в правильной последовательности: от менее ограничивающего к более, потому как без опции
quick последнее совпавшее правило выигрывает, а не первое совпавшее.

Опция quick не используется т.к. при совпадении пакет будет назначен в очередь и пропущен и не дойдет до тех правил, которые описывают разрешения и приоритеты, основанные не на протоколах, а на адресах источника/назначения (в нашем случае это компьютеры из группы pc_hipri и pc_pri). Т.к. протокол ТСР, то так же добавляем очередь для ТСР АСК-пакетов. В качестве адреса назначения в правилах фигурирует !(self:network). Это значит, что только пакеты, не предназначенные ни к одному IP-адресу сетевых интерфейсов или IP-адресу из
подсетей, подключенных к роутеру, будут разрешаться и, соответственно, тегироваться. Это, например, не даст подключиться к внешнему IP из локальной сети.

pass in log on <span class="variable" style="color: #b58900;">$int_if</span> inet proto tcp from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to !(<span class="keyword" style="color: #859900;">self</span>:network)
		<span class="variable" style="color: #b58900;">$mst</span> queue (d_other d_ack) tag INET_OTHER
pass in log on <span class="variable" style="color: #b58900;">$int_if</span> inet proto tcp from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to !(<span class="keyword" style="color: #859900;">self</span>:network)
		port {<span class="number" style="color: #2aa198;">20</span> <span class="number" style="color: #2aa198;">21</span> <span class="number" style="color: #2aa198;">25</span> <span class="number" style="color: #2aa198;">110</span> <span class="number" style="color: #2aa198;">143</span> <span class="number" style="color: #2aa198;">5190</span> <span class="number" style="color: #2aa198;">8080</span> <span class="number" style="color: #2aa198;">081</span>} <span class="variable" style="color: #b58900;">$mst</span> queue (d_lowpri d_ack) tag INET_LOWPRI
pass in log on <span class="variable" style="color: #b58900;">$int_if</span> inet proto tcp from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to !(<span class="keyword" style="color: #859900;">self</span>:network)
		port <span class="number" style="color: #2aa198;">443</span> <span class="variable" style="color: #b58900;">$mst</span> queue (d_pri d_ack) tag INET_PRI



Далее 2 правила, которые будут срабатывать как для всех пользователей так и для тех, кто принадлежит к т.н. VIP-группе (pc_hipri и pc_pri). Поэтому тут используем опцию quick.

pass in log quick on <span class="variable" style="color: #b58900;">$int_if</span> inet proto tcp from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to !(<span class="keyword" style="color: #859900;">self</span>:network)
		port {<span class="number" style="color: #2aa198;">22</span> <span class="number" style="color: #2aa198;">3389</span>} <span class="variable" style="color: #b58900;">$mst</span> queue (d_hipri d_ack) tag INET_HIPRI
pass in log quick on <span class="variable" style="color: #b58900;">$int_if</span> inet proto tcp from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to !(<span class="keyword" style="color: #859900;">self</span>:network)
		port <span class="number" style="color: #2aa198;">53</span> <span class="variable" style="color: #b58900;">$mst</span> queue (d_dns d_ack) tag INET_DNS



Следующие 2 правила, по-аналогии с TCP, описывают разрешения и очереди для протокола UDP. Так же их тегируем. Второе правило с опцией quick, т.к. оно должно срабатывать для всех категорий пользователей.

pass in log on <span class="variable" style="color: #b58900;">$int_if</span> inet proto udp from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to !(<span class="keyword" style="color: #859900;">self</span>:network) queue d_other tag INET_OTHER
pass in log quick on <span class="variable" style="color: #b58900;">$int_if</span> inet proto udp from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to !(<span class="keyword" style="color: #859900;">self</span>:network)
		port {<span class="number" style="color: #2aa198;">53</span> <span class="number" style="color: #2aa198;">123</span>} queue d_dns tag INET_DNS



Правило, разрешающее ICMP.

pass in log on <span class="variable" style="color: #b58900;">$int_if</span> inet proto icmp from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to !(<span class="keyword" style="color: #859900;">self</span>:network) queue d_lowpri tag INET_LOWPRI



Следующие правила, разрешающие трафик от клиентов, с высоким и нормальным приоритетом. Весь трафик будет считаться очень высоким (hipri) или высоким (pri) приоритетом (кроме ДНС и ТСР АСК). Здесь мы не указываем протокол, но указываем modulate state, который применяется только для ТСР. Это не будет ошибкой, PF достаточно «умный» и он подставит modulate state для протокола ТСР, и keep state — для всех остальных протоколов.

pass in log quick on $int_if inet from <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">pc_hipri</span>></span> to !(self:network) $mst queue (d_hipri, d_ack) tag INET_HIPRI
pass in log quick on $int_if inet from <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">pc_pri</span>></span> to !(self:network) $mst queue (d_pri, d_ack) tag INET_PRI



Разрешаем локальный Ethernet к внутреннему интерфейсу роутера.

pass in quick on <span class="variable" style="color: #b58900;">$int_if</span> inet from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to (<span class="variable" style="color: #b58900;">$int_if</span>) queue etherq



Теперь опишем разрешающие правила с внутреннего интерфейса роутера в локальную сеть. Если у вас нет редиректа с Интернета в локальную сеть или нет никаких серверов/клиентов, запущенных на самом роутере и требующих доступ в локальную сеть, то эти правила можно не добавлять. 
Первое правило разрешает трафик с Интернета к указанному IP в локальной сети и назначает очень высокий приоритет входящего трафика. Это третье правило (первое — rdr, второе — разрешающее на внешнем интерфейсе) для редиректа (проброса) порта в локальную сеть.

pass out quick on <span class="variable" style="color: #b58900;">$int_if</span> inet proto tcp from !(<span class="keyword" style="color: #859900;">self</span>) to <span class="number" style="color: #2aa198;">10.11</span>.<span class="number" style="color: #2aa198;">1.2</span> port <span class="number" style="color: #2aa198;">3389</span> queue (d_hipri d_ack)



Правило, разрешающее трафик от внутреннего сетевого интерфейса роутера в локальную сеть. Не важно какой протокол. Весь направляется в очередь etherq.

pass out quick on <span class="variable" style="color: #b58900;">$int_if</span> inet from (<span class="variable" style="color: #b58900;">$int_if</span>) to (<span class="variable" style="color: #b58900;">$int_if</span>:network) queue etherq



Внешний интерфейс. Разрешающие правила для входящего трафика.
Важно, для каждого входящего правило указывать максимальное кол-во стейтов, которые может
создать правило. Это предотвратит исчерпывания всего лимита стейтов одним правилом в случае DoS атаки. 
Последующие 6 правил разрешают входящие ТСР-подключения к определенным портам роутера, а также назначаются соответствующие очереди. Эти очереди будут приоритизировать не входящий трафик, а исходящий. Так же стоит обратить внимание на первые два правила, которые разрешают доступ к FTP серверу на роутере. Передача команд по 21-му порту будет направляться в очередь с большим приоритетом (u_pri), а данные — с меньшим (u_lowpri).

pass in quick on $ext_if inet proto tcp from <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">trust</span>></span> to ($ext_if) port 21 $mst (max 100) queue (u_pri u_ack)
pass in quick on $ext_if inet proto tcp from <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">trust</span>></span> to ($ext_if) port >=49152 $mst (max 100) queue (u_lowpri u_ack)
pass in quick on $ext_if inet proto tcp from <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">me</span>></span> to ($ext_if) port 22 $mst (max 10) queue (u_hipri u_ack)
pass in quick on $ext_if inet proto tcp from <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">me</span>></span> to ($ext_if) port 80 $mst (max 100) queue (u_pri u_ack)
pass in quick on $ext_if inet proto tcp from <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">me</span> /></span> to ($ext_if) port 5900 $mst (max 10) queue (u_hipri u_ack)



Правило, разрешающее входящее подключение с сети Интернет (конкретно с адреса администратора), но не к интерфейсам роутера (внешнему в том числе), а к компьютеру в локальной сети к порту RDP.

pass in quick on $ext_if inet proto tcp from <span class="tag"><<span class="title" style="font-weight: bold; color: #268bd2;">me</span>></span> to !(self) port 3389 $mst (max 10) queue (u_hipri u_ack)



Следующее правило разрешает подключение с любого адреса сети Интернет к VNC-репитеру. В целях безопасности включен трекинг источника (source-track). 

pass in quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet proto tcp from any to (<span class="variable" style="color: #b58900;">$ext_if</span>) port <span class="number" style="color: #2aa198;">5500</span> <span class="variable" style="color: #b58900;">$mst</span>
max <span class="number" style="color: #2aa198;">10</span>,<span class="variable" style="color: #b58900;">$str</span>, max-src-nodes <span class="number" style="color: #2aa198;">2</span>, max-src-states <span class="number" style="color: #2aa198;">3</span>, max-src-conn-rate <span class="number" style="color: #2aa198;">3</span>/<span class="number" style="color: #2aa198;">60</span>)
queue (u_hipri u_ack)




Следующие 2 правила разрешают подключения к определенным портам по протоколу UDP.

pass in quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet proto udp from any to (<span class="variable" style="color: #b58900;">$ext_if</span>) port <span class="number" style="color: #2aa198;">1194</span> (max <span class="number" style="color: #2aa198;">20</span>) queue u_pri
pass in quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet proto udp from <sip_peers> to (<span class="variable" style="color: #b58900;">$ext_if</span>) port <span class="number" style="color: #2aa198;">5060</span> (max <span class="number" style="color: #2aa198;">20</span>) queue u_hipri



Разрешаем пинг к внешнему интерфейсу.

pass in quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet proto icmp from any to (<span class="variable" style="color: #b58900;">$ext_if</span>) icmp-type echore<span class="string" style="color: #2aa198;">q (max 100)</span> queue u_other



Внешний интерфейс. Разрешающие правила для исходящего трафика.

Следующие 5 правил разрешают исходящий трафик с внешнего интерфейса, который был помечен на внутреннем интерфейсе. Это исключительно данные, которые передаются в сеть Интернет от клиентов в локальной сети.

pass out quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet from (<span class="variable" style="color: #b58900;">$ext_if</span>) to any <span class="variable" style="color: #b58900;">$mst</span> queue (u_dns u_ack) tagged INET_DNS
pass out quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet from (<span class="variable" style="color: #b58900;">$ext_if</span>) to any <span class="variable" style="color: #b58900;">$mst</span> queue (u_hipri u_ack) tagged INET_HIPRI
pass out quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet from (<span class="variable" style="color: #b58900;">$ext_if</span>) to any <span class="variable" style="color: #b58900;">$mst</span> queue (u_pri u_ack) tagged INET_PRI
pass out quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet from (<span class="variable" style="color: #b58900;">$ext_if</span>) to any <span class="variable" style="color: #b58900;">$mst</span> queue (u_lowpri u_ack) tagged INET_LOWPRI
pass out quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet from (<span class="variable" style="color: #b58900;">$ext_if</span>) to any <span class="variable" style="color: #b58900;">$mst</span> queue (u_other u_ack) tagged INET_OTHER



И последние правила, разрешающие исходящие соединения с внешнего интерфейса.
Это будет трафик непосредственно самого роутера. В целом приоритет этого трафика выше чем от клиентов в локальной сети.

pass out quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet proto tcp from (<span class="variable" style="color: #b58900;">$ext_if</span>) to any port <span class="number" style="color: #2aa198;">53</span> <span class="variable" style="color: #b58900;">$mst</span> queue (u_dns u_ack)
pass out quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet proto tcp from (<span class="variable" style="color: #b58900;">$ext_if</span>) to any <span class="variable" style="color: #b58900;">$mst</span> queue (u_pri u_ack)
pass out quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet proto udp from (<span class="variable" style="color: #b58900;">$ext_if</span>) to any port {<span class="number" style="color: #2aa198;">53</span> <span class="number" style="color: #2aa198;">123</span>} queue u_dns
pass out quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet proto udp from (<span class="variable" style="color: #b58900;">$ext_if</span>) to <sip_peers> port <span class="number" style="color: #2aa198;">5060</span> queue u_hipri
pass out quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet proto udp from (<span class="variable" style="color: #b58900;">$ext_if</span>) to any queue u_pri
pass out quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet proto icmp from (<span class="variable" style="color: #b58900;">$ext_if</span>) to any <span class="variable" style="color: #b58900;">$mst</span> queue u_lowpri



Есть один важный момент, на котором остановлюсь подробнее. Второе правило будет разрешать ТСР-соединения с роутера и в том числе на 80 порт. Под это правило так же подпадают пакеты, отправленные с прокси-сервера, трафик, который фактически является HTTP-трафиком клиентов из локальной сети. Т.к. в нашем случае приоритеты этих видов трафика равны (очередь u_pri), то все хорошо. Но если планируется назначить очереди разные (например, HTTP от локальных клиентов — очередь u_lowpri, а с внешнего интерфейса роутера — u_pri), тогда следует указать в правиле для прокси-сервера опцию user uid и поместить его над
правилом для внешнего интерфейса роутера. Например, прокси запущен с правами пользователя nobody:

pass out quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet proto tcp from (<span class="variable" style="color: #b58900;">$ext_if</span>) to any user nobody <span class="variable" style="color: #b58900;">$mst</span> queue (u_lowpri u_ack)
pass out quick on <span class="variable" style="color: #b58900;">$ext_if</span> inet proto tcp from (<span class="variable" style="color: #b58900;">$ext_if</span>) to any <span class="variable" style="color: #b58900;">$mst</span> queue (u_pri u_ack)



Соответственно, правило, разрешающее доступ к прокси из локальной сети тоже немного измениться: необходимо поменять очередь с d_pri на d_lowpri.

pass in <span class="keyword" style="color: #859900;">log</span> quick on <span class="variable" style="color: #b58900;">$int_if</span> inet proto tcp from (<span class="variable" style="color: #b58900;">$int_if</span>:network) to <span class="number" style="color: #2aa198;">127.0</span>.<span class="number" style="color: #2aa198;">0</span>.<span class="number" style="color: #2aa198;">1</span> port <span class="number" style="color: #2aa198;">31280</span> queue (d_lowpri, d_ack)



Стоить отметить о наличии бага, который проявляется при использованием опции user. Об этом описано в секции BUGS на странице руководства pf.conf. Лично у меня этот баг не проявлялся. Все же старайтесь опцию user не использовать. 

Отладка. Отличительной способностью PF есть его протоколирование, статистика и мониторинг состояния в реальном времени что значительно облегчает конфигурирование файервола, особенно для сложных сетевых топологий.

Просмотр загруженных очередей:

pfctl -sq



Что бы в реальном времени наблюдать загрузку очередей выполните команду:

pfctl -vvsq



Так же рекомендую установить из портов программу pftop, которая по аналогии с утилитой top выводит различную статистику PF в реальном времени.