Введение (можно пропустить)
Отличие Source Specific Multicast от “обычного”(Any Source Multicast(ASM)) в его сигнализации и фильтрации. Формально, существует 3 модели распространения multicast – ASM, SFM(source filtered multicast) и SSM. Но в действительности, с точки зрения запроса мультикаст-трафика со стороны клиента, SSM можно рассматривать как частный случай SFM. Модель SSM подразумевает, что мультикаст группы принадлежат к диапазону адресов 232.0.0.0/8, а IGMPv3 exclude mode запросы не отправляются клиентом и игнорируются свитчами и роутерами, их обрабатывающими.
В классическом варианте, внутри сети оператора используется протокол PIM для распространения мультикаста между роутерами, при этом задействуются RP(точка(и) рандеву) для регистрации источников (S,G) и получения информации о них роутерами, у которых клиенты запросили мультикаст. Использование IGMPv3 в режиме include mode и отказ от exclude mode позволяет избавиться от этих RP, что упрощает конфигурацию сети и, вообще говоря, повышает надёжность(за счёт понижения сложности). Использование адресов 232.0.0.0/8 позволяет роутерам понять что от них хотят, когда вещают или запрашивают мультикаст от них. Получая такой мультикаст(data-трафик), роутер знает, что его не надо регистрировать на RP (сеть может быть смешанная и использовать 1, 2 или 3 модели распространения multicast), а получая IGMPv3 запрос от клиента(include mode) на запрос канала из сети 232.0.0.0/8, роутер не пытается его интерпретировать как ASM с фильтром(т.е. SFM), а сразу шлёт PIM JOIN для построения SPT, если канал (S,G) ранее не был подключен. В терминологии SSM канал определён как (S,G). В общем случае(в зависимости от оборудования) можно изменить диапазон адресов, относящийся к SSM.
IGMPv3 клиент и генератор multicast
Рассмотрим IGMPv3 в контексте CE-устройства, т.е. как запросить SSM по IGMPv3 и как его обработать.
Сначала речь пойдёт о кроссплатформенных средствах, будет написано простейшее приложение на python (клиент) и использован iperf как генератор мультикаста. Это должно работать в современных версиях Linux, Windows, MacOS, FreeBSD и, возможно, в других ОС. Проверялось на Ubuntu Linux 14.04.
Для того, чтобы нагенерировать трафик с ip.src=192.0.2.1, ip.dst=232.252.0.100, dst.udp_port=12345 можно запустить iperf:
iperf -c 232.252.0.100 -B 192.0.2.1 -u -p 12345 -t 0.2 -l 12
Код приложения-клиента:
#!/usr/bin/env python import socket if not hasattr(socket, 'IP_ADD_SOURCE_MEMBERSHIP'): setattr(socket, 'IP_ADD_SOURCE_MEMBERSHIP', 39) group = '232.252.0.100' port = 12345 bind = '0.0.0.0' source = '192.0.2.1' s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) imr = (socket.inet_pton(socket.AF_INET, group) + socket.inet_pton(socket.AF_INET, bind) + socket.inet_pton(socket.AF_INET, source)) s.setsockopt(socket.SOL_IP, socket.IP_ADD_SOURCE_MEMBERSHIP, imr) s.bind((group,port)) for i in range(0, 3): msg = s.recv(16384) print ":".join("{:02x}".format(ord(c)) for c in msg) #print msg
Это приложение подключается (строки 13-20) к каналу (192.0.2.1, 232.252.0.100), ожидая данные на порту udp/12345 (строки 9 и 20), ждёт 3 таких пакета (строки 22-23), распечатывая их в шестнадцатеричной форме (строка 24). На C это будет выглядеть аналогично, только с кучей кода, не относящегося к бизнес логике(работа с памятью), пример реализации можно подсмотреть в коде VLC-плеера.
Запуска клиента и генератора multicast в Linux
Существует несколько нюансов, относительно запуска клиента и генератора. Например, генератор должен отправлять трафик в нужный интерфейс, клиент же должен отправлять IGMP-запросы в интерфейс в сторону генератора, требуется unicast маршрут для прохождения RPF-проверки или она должна быть отключена. Схема проверки выглядит следующим образом:
Чтобы это сконфигурировать нужно выполнить следующие команды:
// создание nets, интерфейсов и конфигурирование IP-адресов # ip netns add G # ip link add veth0 type veth peer name veth1 # ip link set dev veth1 netns G # ip netns exec G ifconfig veth1 192.0.2.1/30 up # ip netns exec G ifconfig veth1:1 192.0.2.2/30 up # ifconfig veth0 198.51.100.1/30 up //маршрут для прохождения RPF-проверки при получении data-трафика # ip route add 192.0.2.0/30 dev veth0 //маршрут для отправки IGMP-запроса клиентом в нужный интерфейс # ip route add 232.252.0.100/32 dev veth0 //маршрут для отправки data-трафика генератором мультикаста # ip netns exec G ip route add 232.252.0.100/32 dev veth1
Теперь запускаем tcpdump на интерфейсе veth1 и клиентское приложение:
// терминал 1 (вывод появится после запуска приложения из терминала 2) # ip netns exec G tcpdump -i veth1 -n -nn -v -vv -s 0 21:48:58.687747 IP (tos 0xc0, ttl 1, id 0, offset 0, flags [DF], proto IGMP (2), length 56, options (RA)) 198.51.100.1 > 224.0.0.22: igmp v3 report, 2 group record(s) [gaddr 232.252.0.100 allow { 192.0.2.1 }] [gaddr 232.252.0.100 to_in { 192.0.2.1 }] // терминал 2 (запуска приложения) $ ./ssm-client.py // терминал 3 (просмотр multicast фильтра, заданного include mode-ом) # cat /proc/net/mcfilter Idx Device MCA SRC INC EXC 7 veth0 0xe8fc0064 0xc0000201 1 0 # echo 'e8 fc 00 64' | perl -ne 'printf "%d.%d.%d.%d\n", map { hex($_)} split unless /^\s$/' 232.252.0.100 # echo 'c0 00 02 01' | perl -ne 'printf "%d.%d.%d.%d\n", map { hex($_)} split unless /^\s$/' 192.0.2.1
И, наконец, запускам генератор и смотрим что выдаст клиент в терминал:
//терминал 3 # ip netns exec G iperf -c 232.252.0.100 -B 192.0.2.1 -u -p 12345 -t 0.2 -l 12 WARNING: option -l has implied compatibility mode ------------------------------------------------------------ Client connecting to 232.252.0.100, UDP port 12345 Binding to local address 192.0.2.1 Sending 12 byte datagrams Setting multicast TTL to 1 UDP buffer size: 160 KByte (default) ------------------------------------------------------------ [ 5] local 192.0.2.1 port 12345 connected with 232.252.0.100 port 12345 [ ID] Interval Transfer Bandwidth [ 5] 0.0- 0.2 sec 25.3 KBytes 1.04 Mbits/sec [ 5] Sent 2158 datagrams //терминал 2 (ssm-client.py был запущен ранее, см. выше) $ ./ssm-client.py 00:00:00:00:53:cc:07:9a:00:0d:fc:de 00:00:00:01:53:cc:07:9a:00:0e:02:01 00:00:00:02:53:cc:07:9a:00:0e:02:63
Всё ок. Теперь попробуем отправить мультикаст с другого ip.src(192.0.2.2), при этом запрашивать (192.0.2.1, 232.252.0.100), чтобы посмотреть как работает mcfilter:
//терминал 2 $ ./ssm-client.py //терминал 3 # ip netns exec G iperf -c 232.252.0.100 -B 192.0.2.2 -u -p 12345 -t 0.2 -l 12 WARNING: option -l has implied compatibility mode ------------------------------------------------------------ Client connecting to 232.252.0.100, UDP port 12345 Binding to local address 192.0.2.2 Sending 12 byte datagrams Setting multicast TTL to 1 UDP buffer size: 160 KByte (default) ------------------------------------------------------------ [ 5] local 192.0.2.2 port 12345 connected with 232.252.0.100 port 12345 [ ID] Interval Transfer Bandwidth [ 5] 0.0- 0.2 sec 25.7 KBytes 1.05 Mbits/sec [ 5] Sent 2193 datagrams
Трафик отправлен, но не прошёл через mcfiler, приложение его не видит, как и ожидалось.
В идеальном случае, клиент не должен получать незапрашиваемого трафика, однако в случае наличия в сети 2ух каналов вида (S1, G1) и (S2, G1) и подключения двух клиентов(которые запрашивают эти группы) к свитчу, умеющему только L2-мультикаст(на уровне dataplane), такая ситуация будет иметь место, поэтому фильтр трафика на стороне клиента, в общем случае, нужен.
В одной из следующих статей будет рассмотрена маршрутизации мультикаст с помощью IGMPv3-сигнализации, в продолжение предыдущей статьи о статической мультикаст маршрутизации в Linux.
Pingback: Маршрутизация multicast в Linux | Net-Labs.in