среда, 10 февраля 2010 г.

Multicast forward/routing Linux, OpenBSD (iptv).

/*

в эпоху модемов с поддержкой V.22 (1,200 бит/с) даже думать о потоковом контенте было страшно. времена меняются и в медвежьи берлоги вламываются результаты "электрификации всей стганы" с более-менее приемлимыми тарифными планами. при этом вполне оправдано появление устройств из категории "домашние роутеры", принимающих канал доступа в internet/локалку от провайдера и раздающих ресурсы грубо говоря "на всю квартиру". зачастую эту функцию берут на себя старенькие компы уровня Pentium-Pentium III под управлением Linux/OpenBSD. при этом зачастую Провайдер даёт выход в сеть internet при помощи vpn/dsl туннеля/подключения, а мультикаст (как например услуга iptv - потокового вещания видео) "крутится" в сегменте "локальной" сети. ниже постараюсь описать принципы настройки роутера для работы в данных условиях. материал не претендует на исчерпывающее изложение и какую-либо оригинальность. здесь не будет готовых рецептов - только общие рекомендации. если Вам понравилось или есть чем дополнить - милости прошу оставить комментарий.


***********************************************

намеренно оставил за рамками роутеры на FreeBSD, так как imho (хотел бы ошибаться, но практика, Господа...) эта система не содержит механизмов, позволяющих нормально решать вопросы динамического роутинга/форварда мультикаста одновременно с NAT-ом, о чём честно предупреждает handbook:

31.2.8 Multicast Routing

FreeBSD supports both multicast applications and multicast routing natively. Multicast applications do not require any special configuration of FreeBSD; applications will generally run out of the box. Multicast routing requires that support be compiled into the kernel:

options MROUTING

In addition, the multicast routing daemon, mrouted(8) must be configured to set up tunnels and DVMRP via /etc/mrouted.conf. More details on multicast configuration may be found in the manual page for mrouted(8).

Note: As of FreeBSD 7.0 the mrouted(8) multicast routing daemon has been removed from the base system. It implements the DVMRP multicast routing protocol, which has largely been replaced by pim(4) in many multicast installations. The related map-mbone(8) and mrinfo(8) utilities have also been removed. These programs are now available in the FreeBSD Ports Collection as net/mrouted.

также стоит помнить, что сам по себе демон "mrouted" морально устарел (Ben, it's dead! RIP...) и не рекомендован к использованию при наличии альтернатив.

***********************************************


OpenBSD


тут всё очень просто и понятно. сам механизм прекрасно документирован:

OpenBSD Multicasting

"dvmrpd" работает практически без нареканий и не вызывает трудностей при конфигурировании. также доступен и "старый" mrouted, если вдруг по каким-то причинам Вас не устроит dvmrpd. что-то добавить к замечательно изложенному материалу желания не возникает.


Linux


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

вне зависимости от "железа" основную роль тут играют "прошивки". они могут быть закрытыми ("фирменными", что предустановлены на заводе-изготовителе) или открытыми (как dd-wrt, прошивки Олега для ASUS-ов и т.п.). с задачей форварда/роутинга мультикаста "из коробки" на отлично справляются лишь прошивки от Олега (вне зависимости от наличия vpn соединения). по крайней мере до сего дня dd-wrt содержали баг в ядре и в исходниках igmpproxy, препятствующий нормальному функционированию. подробности.

рассмотрим пример конфигурации роутера:

eth0 - интерфейс к локальной сети Провайдера (пусть будет сеть 10.0.0.0/8), обозначим как $IF_IN
ppp0 - vpn туннель с выходом в internet, обозначим как $IF_VPN
eth1 - интерфейс к "домашней" сети (адреса в пределах 192.168.1.0/24), обозначим как $IF_OUT

для "домашних роутеров" схема конфига осложняется тем, что в качестве eth1 (интерфейса к "домашней" подсети) выступает (как правило) бридж (br0), объединяющий Ethernet и Wireless интерфейсы (ессно при наличии этого самого wifi).

теперь немного чтива для любознательных:

общее описание мультикаста (English Wiki)

1. Multicast over TCP/IP HOWTO - старый, но добрый хау-ту.

2. Multicast Routing Code in the Linux Kernel - тоже не Откровение, но даёт понятие о (как минимум) двух стоящих внимания "хомячка" переменных:

/proc/net/ip_mr_vif - список интерфейсов, вовлечённых в обмен мультикаст пакетами
/proc/net/ip_mr_cache - текущий статус MFC (Multicast Forwarding Cache - кэш мультикаст пакетов)

3. Configuring Linux For Network Multicast - начальные сведения о конфигурации Linux ядра для роутинга мультикаста.

собсно с конфигурирования ядра и начнём. в обязательном порядке нам нужны следующие опции:

CONFIG_IP_ADVANCED_ROUTER=y (или же CONFIG_IP_ROUTER=y)
CONFIG_IP_MROUTE=y
CONFIG_IP_PIMSM_V1=y
CONFIG_IP_PIMSM_V2=y
CONFIG_IP_MULTICAST=y
CONFIG_NET_IPIP=y

кроме того может возникнуть необходимость проконтролировать (cat имя_файла) и/или изменить (echo $ЗНАЧЕНИЕ > имя_файла) некоторые дополнительные параметры:

разрешаем форвард ipv4 пакетов:

$ cat /proc/sys/net/ipv4/conf/default/forwarding
1

разрешаем форвард мультикаста:

$ cat /proc/sys/net/ipv4/conf/[ $IF_IN | $IF_OUT ]/mc_forwarding
1

отключаем reverse path filtering:

$ cat /proc/sys/net/ipv4/conf/$IF_IN/rp_filter
0

для ядер 2.6.* может потребоваться принудительное указание "типа"/версии igmp пакетов (варианты значений - 0, 1 или 2, описание есть в исходниках ядра - /usr/src/linux/net/ipv4/igmp.c):

$ cat /proc/sys/net/ipv4/conf/[ $IF_IN | $IF_OUT ]/force_igmp_version
{0,1,2}


с ядром более-менее разобрались, остался вопрос к прикладному софту. потребуется:

iptables
igmpproxy
пакеты "net-tools" (route, traceroute) и "iproute2" (ip) для управления маршрутами/(роутинг)
tcpdump/wireshark для мониторинга


в роли igmpproxy может выступить pimd или проприетарный gated (эт если из пушки да по воробьям засадить...). есть ещё smcroute, но оно умеет только "статику" (не наш метод). ещё на просторах сети где-то бродят сборки "родного" древнего многострадального mrouted-а с патчами для Linux. не будем ворошить труп. RIP.

для начала на роутере очищаем все цепочки iptables (flush) и рисуем что-то типа:

-A INPUT -d 224.0.0.0/240.0.0.0 -p 2 -j ACCEPT # некоторые опускают ключ -d 224.0.0.0/240.0.0.0 и разрешают весь входящий igmp трафик
-A INPUT -d 224.0.0.0/240.0.0.0 -p udp -m udp ! --dport 1900 -j ACCEPT
-A FORWARD -d 224.0.0.0/240.0.0.0 -p udp -j ACCEPT

также можно включить некоторую страховку от сволочной и шкурной натуры Провайдера, принудительно увеличив TTL мультикаст-потока на одну единичку при прохождении нашего роутера (а то особо умные принудительно на мультикаст ставят TTL == 1, чтобы потешить ЧСВ, не иначе):

-t mangle -A PREROUTING -d 224.0.0.0/240.0.0.0 -p udp -j TTL --ttl-inc 1


после чего делаем тупой конфигурационный файл (читаем man!) для igmpproxy (вместо значений переменных $IF_IN/$IF_OUT подставить имена соответствующих интерфейсов ессно):

$ cat /etc/igmpproxy.conf

# good things to begin with :)
quickleave

phyint $IF_IN upstream
altnet 0.0.0.0/0

phyint $IF_OUT downstream


и запускаем демона (от рута):

# igmpproxy -c /etc/igmpproxy.conf


для того, чтобы тело взлетело осталось лишь правильно настроить роутинг/(маршрутизацию). здесь надо помнить, что источники выдачи сигнала могут присутствовать помимо указанных в плейлисте того же iptv. т.е. кроме собсно мультикаста (net 224.0.0.0/240.0.0.0 ) источник может иметь "левые" ip-ы типа 88.210.40.0/24, 77.94.170.0/24 и т.п.. это легко отслеживается tcpdump-ом/wireshark-ом если непосредственно на роутере стартануть VLC или MPlayer, собранный с поддержкой live555 и получить "картинку" видео. роутинг к этим "левым" адресам должен идти через $IF_IN, не затрагивая $IF_VPN! объясняться подобный "казус" может довольно просто. Провайдер зачастую делает пиринг с теми, кто держит нехилые "фермы", раздающие сам трафик, что и приводит к появлению в локальной сети (Провайдера) подобных "левых" адресов. или же "фермам" присваивают адреса "от фонаря". другими словами адрес, что прописан в плейлисте - это адрес для JOIN/LEAVE ("подписки" и "выписки" из мультикаст-группы), а сам источник udp потока может приползти откуда угодно.

напоследок могу лишь посоветовать посматривать на статистику $IF_IN при пользовании igmpproxy. известно, что [далеко] не всегда "отписка" проходит корректно и это вполне может забить канал рано или поздно (при активной смене каналов вы быстро заметите если что-то пойдёт не так...).

удачи.

*/

13 комментариев:

Анонимный комментирует...

Как правило в "городских локалках" не разворачивают RIP или других протоколов динамической маршрутизации, а без этого ни mrouted ни dvmrp не заработают. Так что на *BSD тоже нужет igmpproxy, где можно вручную указать один внешний и произвольное количество внутренних интерфейсов. Есть в портах.

sda комментирует...

спасибо! ещё есть наблюдение, что mrouted не работает вместе с NAT-ом. возможно, что кривые руки, но примеров масса...

Анонимный комментирует...

хорошая заметка, спасибо :)

Анонимный комментирует...

Как вариант - udprxy.

sda комментирует...

огромное СПАСИБО за теплые комментарии. мои наилучшие всем читателям. с прошедшим и Наступающим праздниками.

Антон Касимов комментирует...

Уважаемый автор, а вы пробовали запускать dvmrpd на OpenBSD? Почему не указываете, что работа возможна только при условии использования в сети протокола DVMRP (о чём анонимный комментатор вам ранее написал).

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

sda комментирует...

видимо потому, что:

1) в ссылке на описание механизма работы с multicast-ом для OpenBSD об этом написано чёрным по белому в первом же параграфе (About multicasting)

2) у кого как, но, imho, у 80%-90% Провайдеров, предоставляющих "iptv" или multicast streaming, поднят PIM, что гарантирует нормальную работу с dvmrpd. по крайней мере в наших окраинах работает.

travian комментирует...

вариант - udprxy

Анонимный комментирует...

Для Linux вместо igmpproxy лучше использовать rigelmcr

Анонимный комментирует...

altnet 0.0.0.0/0
Упс.
The bits part of the address is invalid : 6321352.
Unable to parse subnet address.
Unknown token '0.0.0.0' in configfile
Unable to load config file...
В чём прикол?

Анонимный комментирует...

Не выходит каменный цветок.
Конфигрурация ядра, интерфейсов идентичная (с поправкой на имена интерфейсов).
Клиент нормально подписывается, но UDP трафик "лезет" в исходящий интерфейс (в Вашем случае это будет eth0)

Анонимный комментирует...

Не выходит каменный цветок.
Конфигрурация ядра, интерфейсов идентичная (с поправкой на имена интерфейсов).
Клиент нормально подписывается, но UDP трафик "лезет" во входящий интерфейс (в Вашем случае это будет eth0)

Анонимный комментирует...

Странный подход, навеянный Биллушкой ("И кухарка будет управлять компьютерами"), IMHO...
Странность подхода в том, чтобы не использовать стандартные средства ОС (например, в linux это - возможности ядра + админские [userland] стандартные заморочки "из командной строки"), А ИСКАТЬ ПРИБЛУДУ, КОТОРАЯ ПОРЕШАЕТ ВАШИ ПРБЛЕМЫ.

Ещё раз, это - IMHO. Ежели кого зацепло - прошу пардона...