Предисловие


Недавно мною было замечено, что при просмотре мультикастового IPTV через Wi-Fi часть трафика теряется. После детального изучения проблемы было выяснено, что такое поведение объясняется природой мультикаст-трафика, а именно – MAC-адрес получателя пакета. Он не зависит от получателя и формируется из адреса мультикаст-группы. Соответственно, на такие пакеты претендуют все клиенты, подключенные к беспроводной точке доступа. Вследствие этого нам достается лишь часть пакетов и мы видим обрывистую картинку.

Штатными средствами проблема решается либо созданием отдельной точки доступа для клиента, либо созданием статического маршрута для определенных мультикаст-групп, или же выведением клиента в отдельный VLAN. Вся “сила” таких решений проявится, когда в сети будет несколько IPTV-приставок, желающих посмотреть один и тот же канал, плюс необходимость их в интернете добавит сложность к настройке роутера. Свое решение данной проблемы предлагаю ниже.

Программы типа udpxy здесь не подходят, так как они меняют полную структуру пакета. А нам необходимо лишь установить необходимый MAC-адрес, при этом сохраняя сетевую и транспортную части, чтобы клиентское ПО не заметило никаких изменений.

Данное решение, назовем его MUT (Multicast to Unicast Translation), заключается в следующем:
  1. Узнать IP-адрес клиента, желающего подключиться к группе
  2. Сообщить об этом ядру ОС
  3. Узнать по IP-адресу MAC-адрес клиента
  4. Создать и отправить копию пакета на соответствующий интерфейс

Выполнение шагов 1 и 2 лежит на программе мультикастовой маршрутизации, 3 и 4 – на ядре. И то и другое требует небольших изменений в своей работе. Вся работа будет проходить в ОС GNU/Linux.

Немного теории


Сетевая маршрутизация IP версии 4 в Linux базируется на следующий структурах:
  • sk_buff – самая часто используемая структура и представляет из себя весь сетевой пакет. Она передается из функции в функцию по пути меняя свое содержимое.
  • rtable + dst_entry – две структуры, хранящие результат кэширования маршрута, полученный из таблицы маршрутизации. В зависимости от адреса получателя, адреса источника и поля TOS пакета определяется дальнейшая политика по отношению к нему. Эти две структуры хранят важную информацию для нас: интерфейс, через который будет проходить отправка, и поле шлюз — будущий L2-сосед, которому можно отправить пакет, не меняя L3-заголовок. Поиск кэша для каждого кадра производится два раза: один раз на входе (входящий трафик) и второй раз на выходе (исходящий). Нас интересует второй.
  • neighbour – каждый экземпляр этой структуры представляет собой L2-соседа для определенного IP-адреса получателя. Он содержит MAC-адрес получателя, полученный после ARP-ответа; очередь из sk_buff, которые необходимо отправить после определения MAC-адреса; таймеры и многое другое. Для мультикаст-групп соседи тоже создаются, только MAC-адрес генерируется функцией. Нам же следует избежать этого.

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

Схематическое представление mfc_cache-списка:

image
Изображение взято из книги “Linux Networking Architecture”

Разработка


За основу было взято ядро Linux 3.18. Для хранения IP-адресов клиентов для каждой мультикаст-группы расширяем mfc_cache связанным списком:

struct mut_dst {
    struct list_head list;
    __be32 ip;
    struct rcu_head rcu;
};

Вводим новую функцию ipmr_unicast_xmit. В ней будет генерироваться юникастовый rtable, но передавать при этом будем мультикастовый sk_buff. Таким образом мы выбираем необходимый интерфейс для будущей отправки.

Теперь для того, чтобы в дальнейшем был создан neighbour для нашего получателя, а не для мультикаст-группы, в rtable указываем шлюз. За это отвечает поле rt_gateway:

struct rtable *rt;

rt = ip_route_output_ports(net, &fl4, NULL, m_dst->ip, 0, 0, 0, IPPROTO_IPIP, RT_TOS(iph->tos), 0);

if (IS_ERR(rt))
    goto out_free;

rt->rt_gateway = m_dst->ip;
dev = rt->dst.dev;

Вводим sysctl-переменную /proc/sys/net/ipv4/mut. Она даст возможность смены режима работы ядра “на лету”.

Справка
sysctl net.ipv4.mut=1 – Включает новый режим
sysctl net.ipv4.mut=0 – Возвращает режим стандартной маршрутизации

Как и раньше можно посмотреть список маршрутов, теперь еще и unicast:

root@multicast:~# cat /proc/net/ip_mr_cache
Group     Origin Iif Pkts  Bytes    Wrong Dsts
0520C3EF           2 18842 25323648     0 01000A0A

Подробнее со всеми изменениями можно ознакомиться в репозитории. Ссылка в конце статьи.

Наглядное представление работы (изменения в колонке с MAC-адресом):



Маршрутизатор


За основу взята программа IGMPProxy. Можно было взять любую другую, тот же mrouted. Очень важно, что все IGMP-сообщения отправляются от IP-адреса запрашивающего интерфейса, и нам ни что не мешает его использовать. Подробности изменений описывать смысла нет, их также можно найти в соответствующем репозитории. Главное то, что в управлении ядра появляются две новые команды, которые должна поддерживать программа:

  • MRT_MUT_ADD_DST (212) — добавление получателя
  • MRT_MUT_DEL_DST (213) — удаление получателя

Вместе с ними передается структура вида:

struct <name> {
	struct in_addr group; 		// Адрес группы
	struct in_addr origin;		// Адрес источника
	struct in_addr destination;	// Адрес клиента
}

Предупреждение


Стоит заметить, что такой подход не дает возможности отключать клиентов от групп за отсутствие от них Membership Report-запросов, так как, исходя из протокола IGMP, клиент, получивший от другого клиента такой запрос с той же группой, сам не отправляет аналогичный. Поэтому отключение возможно только после получения явного Leave Group-пакета.

Использование


Для включения новой возможности необходимо скомпилировать ядро с опцией CONFIG_IP_MUT=y
Для полноценной работы измененной IGMPProxy также необходимо включить CONFIG_SYSCTL_SYSCALL=y

Ссылки


Измененное ядро
Измененный IGMPProxy

Использованная литература


Rami Rosen «Linux Kernel Networking. Implementation and Theory»
Christian Benvenuti «Understanding Linux Network Internals»
Klaus Wehrle and Frank Pahlke «Linux Networking Architecture»

Если у кого-нибудь есть иной способ решения проблемы, прошу поделиться в комментариях.
Поделиться с друзьями
-->

Комментарии (27)


  1. Karroplan
    09.06.2016 10:56
    +3

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

    это звучит как минимум странно и как максимум совершенно неверно. Подозреваю, что вы не полностью разобрались. wiFi является средой с одновременным множественным доступом. Соотвественно broadcast/multicast пакеты слышат все клиенты и принять могут тоже все, в зависимости от конкретных условий приема. То, что кто-то из клиентов таки принимает multicast-пакет с широковещательным dst mac-адресом группы не удаляет этот пакет из эфира (чудес не бывает).


    1. q1b
      09.06.2016 12:59

      Согласен, причину падения трафика не рассмотрел должным образом.


    1. nevzorofff
      09.06.2016 13:30

      Вот-вот. Я тогда не понял — в чём сложность мультикаста и wifi в среде с общим доступом-то?


      1. dmitrmax
        09.06.2016 18:29

        В том, что WiFi в общем случае организует надежную доставку сообщений. За исключением broadcast и multicast трафика. В итоге если клиент получает битый мультикастовый фрейм, то он не будет перепослан.


        1. nevzorofff
          10.06.2016 00:01

          Разве мультикаст где-то перепосылается?


          1. dmitrmax
            10.06.2016 00:10

            Какой-то вопрос у вас странный. Я же написал выше, что в WiFi мультикаст не перепосылается. Но если вы, например, будете транслировать мультикаст через VPN-tunnel-over-TCP, то он будет замечательно перепосылаться на участке VPN, если будут потери. Потому что VPN в данном случае для мультикаста — это уровень L2, который организует надежную доставку независимо от payload.

            Отвечая на ваш вопрос более коротко — зависит от L2.


            1. nevzorofff
              10.06.2016 07:48

              Только мультикаст в вашем примере к перепосылке не имеет никакого отношения. Перепосылаться будет L2 пакет, а не мультикаст.


              1. dmitrmax
                10.06.2016 09:40

                Какой вопрос, такой ответ.


      1. dmitrmax
        09.06.2016 18:34

        В противном случае, точке доступа пришлось бы отслеживать кол-во клиентов, которые подписаны на этот мультикаст через вайфай и количество полученных ack'ов. А ещё каждый новый фрейм вызывал бы шквал ack'ов: по одному от каждого клиента, что приводило бы к частым коллизиям при получении доступа к среде, испорченным (из-за наложения) ack'ам. Испорченные ack'и вызывали бы повторные не нужные перепосылки multicast'а. И всё по кругу.


        1. nevzorofff
          10.06.2016 07:49

          В мультикасте не ACK на каждый пакет, там по таймеру(измеряемый в секундах)посылается пакет для подтверждения подписки.


          1. dmitrmax
            10.06.2016 09:49

            Вы вообще путаете тёплое с мягким. Я вам рассказываю о том, как работала бы надежная доставка в WiFi, если бы она была включена для multicast, а вообще сейчас говорите о протоколе IGMP. Это вообще про разное.

            В мультикасте вообще нет ACK. ACK есть в WiFi, есть в TCP. А мультикаст — это вообще не протокол, это способ адресации всего лишь. Как правило используется для UDP трафика, но не только. Какая-то у вас каша.


    1. dmitrmax
      09.06.2016 18:21

      Скорее всего для мультикаст траффика не работает механизм надежной доставки WiFi.


      1. nevzorofff
        10.06.2016 07:50

        Он и не нужен. Пока пакет приедет повторно — он уже окажется ненужен.


        1. dmitrmax
          10.06.2016 09:44

          Ваше утверждение как минимум спорно. Потому что когда через WiFi вы имеете дело с таким же трафиком, но unicast, то таких лагов, как у автора не наблюдается. Учитывая, что вероятность успешной передачи пакета не зависит от его типа (u-cast vs. m-cast), то это означает, что WiFi механизмы в случае unicast работают и работают успешно.


        1. dmitrmax
          10.06.2016 12:20

          И ещё, вы говорите только о риалтайм трафике (голос/видео), передаваемых через UDP, как правило. Да будет вам известно, что мультикаст применяется ещё в куче протоколов. Тут вы найдёте не полный список протоколов, который используют мультикаст и которым зачастую до одного места риалтаймовость. Кстати, сам протокол IGMP, тоже работает через multicast.

          А ещё мультикаст применяют на биржах, для рассыки рыночной информации. И там пропуск одной дейтаграммы будет означать невозможность накатить обновления. Для этого там существуют TCP-сервисы, через которые можно запросить пропущенные номера пакетов.

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


  1. Ivan_83
    09.06.2016 12:04
    +3

    Костылинг.

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

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

    udpxy конечно уже мусор, в том смысле что никаких настроек там нет и оно форкается на каждого клиента.
    Поэтому я написал свой софт для UDP->HTTP@TCP: http://www.netlab.linkpc.net/wiki/ru:software:msd:lite
    никаких лишних действий не делает: все кто смотрит один поток получают его из одного общего буфера, используется sendfile() для отправки чтобы не копировать память в буфера сокетов и пр…
    Плюсом когда подключается второй и последующий клиенты они получают сразу порцию данных из того что есть в кольцевом буфере и воспроизведение начинается немедленно на любых устройствах.


    1. calvin_rus
      09.06.2016 12:54

      > Поэтому я написал свой софт для UDP->HTTP@TCP: www.netlab.linkpc.net/wiki/ru:software:msd:lite

      Интересно, надо попробовать. В IPTV новичек.

      А на сколько оно лучше по производительности? Udpxy ругают за то, что после определенного числа клиентов оно начинает тормазить, вне зависимости от мощностей железа. Честно сказать, юзаем udpxy на небольшом числе клиентов, и проблем нет. Тестировали ли данный софт на больших нагрузках?


      1. Ivan_83
        09.06.2016 15:22

        Оно должно быть шустрее nginx раздающего статику с tmpfs, ну или примерно такое же, в пределах стат погрешностей.
        Тестировали, 10G раздать не проблема с этим.
        Единственно что опций слишком много, народ ленится прочитать мануал а дефолтный конфиг далеко не всем подходит.

        2 q1b:
        Так вайфай не гарантированная среда, можно вам целые дни напролёт ныть в поддержку что картинка подсыпается, при использовании любых технологий костыления, в тч HTTP/tcp.

        Маршрутизации в л2 нет, вы пишите туда дст мак хоста и отсылаете что было, а был там л3 мультикаст адрес, который не маршрутизируется.

        Поток то вы размножаете явно не в приставке.

        С точки зрения клиента использование TCP позволяет:
        — иметь более простую инфраструктуру: без свичей и точек доступа со снупингом), и при этом не иметь проблем что мультикаст лезет во все щели
        — не нужен особенный роутер у которого китайцы позаботились о правильной работе IGMP
        — больше устройств может скушать такой поток: все DLNA только http@tcp и жрут (правда обычно нужно приправлять специальными http заголовками, моя прога это делает)
        — поток легче перенаправить куда нужно: можно пробросить порт в инет и самому смотреть ТВ вне дома или дать посмотреть друзьям/родственникам
        — кратковременные перебои на вайфае не приведут к рассыпаниям
        — если провайдер правильно раздаёт сам по http@tcp то у клиента ещё переключение каналов будет происходить максимально быстро, за счёт того что провайдер может сразу присылать 4-8 мегабайт для заполнения буфера и начала воспроизведения.


    1. q1b
      09.06.2016 13:41

      За мультикаст отдаваемый клиентам я бы бил по рукам, ибо клиентам этот мультикаст девать некуда, им юникаст много удобнее

      В статье речь шла о передаче трафика от роутера до приставки. Об отдаче клиентам мультикаста по WiFi речи и не было.

      Маршрутизация и ретрансляция это немного разные понятия

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

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

      Простите, а зачем на IPTV-приставке дальнейшее размножение потока?

      Поэтому я написал свой софт для UDP->HTTP@TCP

      Опять же, если у вас неизменяемый список каналов из мультикаст-адресов, полученные от вашего провайдера, то чем вам поможет UDP->HTTP@TCP? Я говорю как клиент, а не провайдер.


    1. dmitrmax
      09.06.2016 18:24

      > За мультикаст отдаваемый клиентам я бы бил по рукам, ибо клиентам этот мультикаст девать некуда, им юникаст много удобнее.

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


      1. Ivan_83
        09.06.2016 21:05

        Тут целый топик про за/против:
        http://forum.nag.ru/forum/index.php?showtopic=89891&st=40&p=910429&#entry910429
        и тут кратко: http://forum.nag.ru/forum/index.php?showtopic=91028&st=0&p=917752&#entry917752

        2 RicoX:
        Так в полной версии можно прописать канал и к нему пачку источников%
        http://www.netlab.linkpc.net/wiki/ru:software:msd:config#channellist
        URL будет вида: АДРЕС/channel/ИМЯ_КАНАЛА
        я обычно добавляю в конце .ts чтобы под виндой с матроска сплиттером тоже можно было смотреть.


        1. RicoX
          10.06.2016 10:58

          Провтыкал значит эту возможность, а может и возможность авторизации для абонентов есть, а я не вижу?


          1. Ivan_83
            10.06.2016 11:24

            Своими средствами пока нет.
            Можно ставить nginx как прокси, и на нём делать. Я делал с помощью списков IP, каждому адресу сопоставлена своя группа каналов.


    1. RicoX
      09.06.2016 20:50

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


      1. calvin_rus
        09.06.2016 22:53

        Прошу прощения, но чем это черевато? Допустим, можно из этой информации узнать поставщика контента. Но как навредить этим можно?


        1. Ivan_83
          10.06.2016 01:08

          1. Некоторые начинают сканировать весь 224/4, в некотоых конфигурациях это приводит как минимум к быстрому исчерпанию файловых дескрипторов, как максимум там приходит OOM Killer и больше кина нет.

          2. Далеко не всегда каналы берут по честному для вещания, иногда и у конкурентов.
          Впрочем там ещё и в самом потоке много инфы, но по адресу либой админ сразу поймёт, а для разбора потока нужно ещё знать что поставить и куда смотреть.


        1. RicoX
          10.06.2016 10:51

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