SIP через NAT

Эта статья является переводом статьи OpenBSD pf и голос поверх IP

Подоплека

В типичной локальной сети устройства NAT скрывают ряд устройств за одним глобальным IP адресом, выделенным из пространства адресов провайдера. Хоть VOIP и доступен конечному потребителю по протоколу SIP, он (SIP) не может быть напрямую использован за устройствами NAT. Большинство VOIP провайдеров используют так называемые «медиа прокси», наборы серверов перенаправляющих медиа трафик от клиентов к SIP серверам VOIP провайдеров. Данный подход имеет две проблемы: Медиа прокси должен иметь достаточную пропускную способность и низкие задержки, ну и в конечном итоге не более одного SIP устройства на IP адрес клиента.
В домашней локальной сети использовать несколько SIP устройств через медиа прокси становится невозможным. Вместо этого в домашней сети можно настроить NAT который будет перенаправлять голосовой трафик и пакеты управления SIP на соответствующие IP телефоны в локальной сети. Пакетный фильтр OpenBSD может справиться с этой задачей. Можно также запустить локальные PBX или SIP маршрутизатор, но это выходит за рамки данной статьи.

Операционная система

Все ниженаписанное было испытано с FreeBSD 5.2 и выше, с интегрированным в систему PF. В FreeBSD 5.3 и ниже существует фатальная ошибка в директиве бинат pf(4), а также не был полностью интегрирован ALTQ. Используйте FreeBSD 5.4 или выше.

Конфигурация системы

На OpenBSD все, что нужно есть по умолчанию, однако на FreeBSD нужно добавить опции в ядро. Ниже приведены дополнительные опции для ядра.

device          pf                                                              
device          pflog                                                           
device          pfsync                                                          
options         ALTQ
options         ALTQ_CBQ
options         ALTQ_RED
options         ALTQ_RIO
options         ALTQ_HFSC
options         ALTQ_CDNR
options         ALTQ_PRIQ

Конфигурация телефона

Написанное ниже было протестировано с телефоном Cisco 7960.
NAT прокси и outbound_proxy не используются. В телефоне настроен прокси-сервера SIP, и порт контроля 5060/udp. Функция STUN в телефоне включена, хотя некоторые коммерческие SIP прокси могут работать без него.

Конфигурация pf

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

# Вернет ошибку, порт заблокирован
set block-policy return
 
# тайм-аут UDP сессии должен быть равен или больше, чем время регистрации SIP 
# Таймер тайм-аута. Обычно достаточно 300 секунд.
set timeout { udp.first 300, udp.single 150, udp.multiple 900 }
 
# переменные
int_if = "fxp0"
ext_if = "fxp1"
int_net = "192.168.1.0/24"
ipphone1 = "192.168.1.18"
ipphone2 = "192.168.1.19"
 
# Вкючим очереди для внешнего интерфейса. Отделим голосовой трафик от данных
altq on $ext_if hfsc bandwidth 512Kb queue { q_voice, q_other }
queue q_voice bandwidth 3.84Kb priority 6 hfsc(realtime 96Kb)
queue q_other bandwidth 416Kb hfsc { q_pri, q_std, q_low }
queue q_pri   bandwidth 200Kb priority 3 hfsc(red realtime 64Kb)
queue q_std   bandwidth 200Kb priority 2 hfsc(default red )
queue q_low   bandwidth 3.84Kb priority 1 hfsc(red )
 
# Для каждого IP- телефона свое правило трансляции nat.
# Параметр static-port нужен для сохранения временного порта UDP.
# Это нужно чтобы удаленный SIP прокси знал к какой сессии привязан наш IP телефон.
nat on $ext_if proto udp from $ipphone1 to any -> ($ext_if) static-port
nat on $ext_if proto udp from $ipphone2 to any -> ($ext_if) static-port
 
# Правило NAT для остальных устройств локальной сети
nat on $ext_if from $int_net to any -> ($ext_if)
 
pass in  quick on lo0 all
pass out quick on lo0 all
 
# Разрешаем SIP трафик с телефонов на локальном интерфейсе 
pass in  quick on $int_if proto udp from $ipphone1 to any tag VOIP keep state
pass in  quick on $int_if proto udp from $ipphone2 to any tag VOIP keep state
 
pass in  quick on $ext_if proto tcp from any to any port 22 keep state \
  queue(q_std,q_pri)
pass in  quick on $ext_if proto tcp from any to any port 80 keep state \
  queue q_low
 
pass out quick on $ext_if tagged VOIP queue q_voice keep state
pass out quick on $ext_if proto tcp from any to any port 22 keep state \
  queue(q_std,q_pri)
pass out quick on $ext_if proto tcp from any to any flags S/SA keep state \
  queue(q_std,q_pri)
pass out quick on $ext_if proto udp from any to any port 53 queue q_pri \
  keep state
 
# Разрешаем с внешнего интерфейса наружу  tcp, udp, icmp
pass out quick on $ext_if proto { tcp, udp, icmp } all keep state
 
block in log all

Автоматический запуск

Добавить в файл /etc/rc.conf:

pf_enable="YES"                 # Установка в YES запусит пакетный фильтр (pf)
pf_rules="/etc/pf.conf"         # Файл с правилами для pf
pf_program="/sbin/pfctl"        # Путь к файлу pfctl
pf_flags=""                     # Флаги для запуска pfctl
pflog_enable="YES"              # Установка в YES включит логирование для pf
pflog_logfile="/var/log/pflog"  # Путь к лог файлу pf
pflog_program="/sbin/pflogd"    # Путь к файлу pflogd
pflog_flags=""                  # Флаги для запуска pflogd

Устранение неисправностей и проверка

Чтобы убедиться, что все получилось, можно создать медиа поток из локальной сети, он должен NATироваться и быть передан на внешний шлюз SIP. Порты источника, назначения для управления SIP (порт назначения 5060) и медиа трафика (изменяемый) должны оставаться неизменными на шлюзе. И наконец, ваши телефоны должны работать:)
Чтобы проверить, приоритизацию пакетов, нагрузите канал и одновременно используйте IP телефон. Трафик IP телефона должен получить приоритет над остальным трафиком и качество передачи голоса должно быть удовлетворительным. Из-за достаточной загрузки канала очереди как правило не нужны и для FreeBSD вполне достаточно пересылки пакетов.
Проверить очереди: pfctl -vsq -v
Очистить таблицы состояний: pfctl -F state
Проверка правил: pfctl -s rules -v

работает

работает

Данный конфиг

Данный конфиг рабочий?

hfsc корректно с SIP/RTP работает?