IPv6 для профессионалов

Управление групповым трафиком

Разбить на страницы
Показывать лекцию целиком

"Ешь, кума, девятую шанежку — я ведь не считаю". (Пословица)

Между групповым вещанием в IPv4 и IPv6 нет принципиальных отличий благодаря целенаправленной конвергенции их линий развития. Поэтому, даже если читатель не до конца знаком со всеми механизмами группового вещания IPv4, данный раздел не вызовет у него затруднений, потому что он не опирается на знания из IPv4. Как раз наоборот, у такого читателя будет возможность наверстать отставание в данной теме сразу для обеих версий IP.

С приходом IPv6 групповое вещание больше нельзя считать второстепенным механизмом и обходить его вниманием даже при начальном изучении темы — ведь без него IPv6 практически неработоспособен.

"Конструируя" такой важный механизм IPv6, как проколы ND, мы активно применяли групповое вещание, практически не задумываясь над тем, готово ли оно служить нам без каких-либо усилий с нашей стороны. Дабы потом не обнаружилось, что мы построили замок на песке, нам надо уделить немного внимания нашему верному помощнику и убедиться, что у него есть все необходимое для полноценной работы.

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

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

  • В первой из них канальный уровень поддерживает групповое вещание, по крайней мере, в теории. К примеру, идеальная ЛВС Ethernet готова разослать групповой кадр, как только мы укажем верный адрес назначения MAC, в котором установлен бит I/G. На практике же необходимы различные оговорки. Пока сеть Ethernet реализована как общая шина, групповой кадр попадает на вход ко всем станциям, и задача фильтрации группового трафика ложится на сетевые адаптеры. Это означает, что аппаратные фильтры надо явным образом программировать, а когда число групп превышает возможности фильтра, то остается только отключить фильтр. В коммутируемой сети Ethernet проблема неизбирательной доставки групповых кадров по-прежнему остается, потому что коммутаторы заранее не знают, к каким портам подключены члены данной группы. Чуть позже мы посмотрим, как ее можно решить и чего это будет нам стоить.
  • Во второй группе находятся широковещательные канальные технологии без групповой адресации. Типичный представитель этой группы — ныне вышедшая из употребления ЛВС ARCNET. У каждой станции ARCNET был свой канальный адрес длиной 8 бит. Кроме того, нулевой адрес был широковещательным. Однако групповых адресов канального уровня у ARCNET вообще не было. Поэтому групповые адреса сетевого уровня оставалось транслировать в широковещательный адрес ARCNET. Вполне естественно, что эта группа канальных технологий тоже страдает от неизбирательной доставки группового трафика.
  • Третью группу составляют каналы "точка-точка", в которых вся адресация — неявная, например, PPP. Когда локальный узел передает пакет в такой канал, он тем самым как бы заявляет: "Пакет адресован не мне". Канальный уровень, за неимением других вариантов, делает такой вывод: "Раз не мне, то удаленному узлу". И снова групповой пакет попадает на вход узлу, членство которого в группе возможно, но далеко не гарантировано.
  • Наконец, четвертая группа — это каналы NBMA, в которых полноценное групповое вещание невозможно (см. §5.2). Точнее, в них возможно только ограниченное групповое вещание, когда узел рассматривает канал NBMA как множество каналов "точка-точка" и передает групповой пакет всем своим непосредственным соседям. То, что некоторые узлы канала вообще не получат пакет, хотя они, может быть, и состоят в данной группе, — проблема данного типа каналов, и мы с ней вряд ли что-то можем поделать, не меняя тип канала с NBMA на широковещательный при помощи дополнительного протокола (например, LANE в ATM или VPLS в MPLS). В то же время, и проблема неизбирательной доставки тоже остается, потому что узел-источник не обладает сведениями о членстве соседей в данной группе. Хотя такой режим вещания можно назвать групповым только условно, он может пригодиться, например, хостам для розыска маршрутизатора по умолчанию, так как этот маршрутизатор — всегда сосед хоста (см. §5.2).
  • Сухой остаток из этого сравнения таков: если канал вообще поддерживает групповое вещание, то он сделает все возможное, чтобы доставить групповой пакет всем членам группы, но он вправе пожертвовать избирательностью, доставив пакет и узлам, в данной группе не состоящим. Поэтому узел обязан фильтровать входящий групповой трафик по адресу назначения на всех доступных ему уровнях стека. Так как возможность фильтровать его на канальном уровне ограничена, в основном из-за рудиментарной групповой адресации, главная доля ответственности ложится здесь на сетевой уровень, то есть IP.

    Мы сформулировали это требование фильтрации пакетов на входе для группового трафика, потому что именно о нем сейчас идет речь. Однако на практике то же самое требование справедливо и для индивидуального трафика. Так, хост не должен полагать, что любой входящий пакет адресован именно ему. Хост может получать чужие пакеты по ошибке, например, из-за неправильных или устаревших маршрутов в сети. Требования устойчивости и безопасности в этом случае очевидны: прежде чем принять пакет в обработку, хост должен убедиться, что адрес назначения пакета — действительно его. Приведем аналогию из обычной жизни: перед тем как вскрыть найденный в почтовом ящике конверт, хорошим тоном будет проверить адрес получателя.

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

    Строго говоря, нет абсолютной гарантии, что внутри принятого группового кадра окажется именно групповой, а не индивидуальный пакет. Как мы упоминали в примечании к §5.2, некоторые семейства протоколов за пределами TCP/IP даже пользуются этим трюком, чтобы сэкономить на явном разрешении индивидуальных адресов. К их числу относится, например, ISO.

    В то же время, обратная операция может потребовать дополнительных усилий, если канальный уровень поддерживает явную адресацию. Какой канальный адрес назначения мы укажем в групповом кадре? Как раз здесь нам пригодятся правила разрешения групповых адресов IPv6 для данного типа канальной инкапсуляции, а они заведомо локальны и сводятся к какому-то вычислению (cм. §4.1.1). Скажем, в ARCNET любой групповой адрес IPv6 превратится в адрес 0 [§7 RFC 2497], а в Ethernet это будет полученный подстановкой битов адрес 33 33 XX XX XX XX [§7 RFC 2464]. Хотя даже в последнем случае отображение адресов далеко от взаимной однозначности, это не вызовет проблем благодаря обязательной фильтрации на входе.

    На этот аспект можно посмотреть и с другой стороны: потеря информации при разрешении групповых адресов IP в канальные адреса — еще одна весомая причина фильтровать входящие групповые пакеты по их адресу назначения. Это касается не только IPv6, но и IPv4. Так, групповой адрес IPv4 тоже отображается с потерей битов, например, в тот же нулевой адрес ARCNET [§4.3 RFC 1201] или в адрес MAC 48 01-00-5E-XX-XX-XX [§6.4 RFC 1112].

    Наконец мы снова вынырнули на сетевой уровень. Здесь задача группового вещания IPv6 сводится к трем очевидным подзадачам, которые уже знакомы нам по индивидуальной передаче:

  • передача в пределах канала;
  • прием;
  • маршрутизация.
  • С передачей по каналу мы уже практически разобрались. Передающий узел (источник или маршрутизатор) выбирает по каким-то правилам выходной сетевой интерфейс, а затем просто отдает групповой пакет в ведение соответствующего канального модуля. Тот, если нужно, вычисляет канальный адрес назначения и — в добрый путь!

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

    В групповом вещании IP передача и прием — операции независимые, так что вещать группе вполне может узел, в ней не состоящий. Членство в группе влияет только на прием.

    Прекрасно, групповое вещание IPv6 по каналу уже работает. Теперь попробуем выйти за пределы одного канала и подумаем над маршрутизацией групповых пакетов. Допустим, у группы есть члены за пределами канала, к которому подключен источник. Понадобится ли источнику таблица групповых маршрутов, или хотя бы групповой маршрутизатор по умолчанию? Придется ли ему отдельно работать с маршрутизатором и членами группы на канале? На самом деле, всех этих сложностей можно избежать, если маршрутизатор сам притворится одним из членов группы и станет принимать групповой трафик наравне с ними (см. рис. 8.1), вместо того чтобы служить явным следующим шагом (next hop) групповых пакетов. Поскольку маршрутизатор на самом деле не потребляет принятые групповые пакеты сам, а продвигает их дальше, признать его полноценным членом группы мы не можем. Он — только слушатель (listener) данной группы. Действительные члены группы — тоже ее слушатели, однако не все слушатели группы — ее члены.

    (рис 8.1) С точки зрения источника, групповые маршрутизаторы неотличимы от хостов

    Чтобы такая схема работала, групповой маршрутизатор должен каким-то образом узнать, какие группы ему слушать и куда их продвигать. Конечно, эти сведения можно сообщить ему путем ручной настройки, но это плохо вяжется с динамическим характером группового вещания. Допустим, маршрутизатор соединяет несколько каналов: К1, К2, К3,… КN. Если текущий источник группы Г находится на канале К1, а слушатели есть только на К2, то маршрутизатор должен слушать группу Г, как минимум, на К1 и продвигать ее трафик в К2. Самое примитивное решение могло бы состоять в том, чтобы слушать группу Г на всех N каналах, а продвигать групповой пакет в N - 1 каналов, за исключением того, откуда пришел данный пакет. Это напоминает нам процесс лавинной рассылки (flooding) в работе коммутатора ЛВС.

    Возможно ли оптимизировать этот процесс? Как мы помним, обучаемый коммутатор ЛВС извлекает информацию о местонахождении узла из того, через какой интерфейс (порт) приходят от него кадры, в предположении, что путь к данному узлу совпадает с путем от него. Поэтому коммутатор делает, к примеру, такой вывод: "Если кадр от источника У2 пришел через интерфейс И5, то впредь я стану продвигать кадры, адресованные У2, только в интерфейс И5". Но, увы, сейчас нам от этого трюка нет никакой пользы. Ведь входной интерфейс группового пакета не дает маршрутизатору никаких сведений о том, где находятся слушатели группы. Корень проблемы здесь кроется в том, что слушатели никак себя не проявляют, а косвенных признаков явно недостаточно. Во-первых, источник группового пакета — далеко не всегда слушатель этой группы. Во-вторых, групповой адрес никогда не возникнет в поле "источник", потому что архитектура IP не допускает коллективного авторства пакетов. В-третьих, не все слушатели группы — ее члены и могут говорить от имени группы.

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

    Чтобы восполнить этот недостаток информации, слушателям группы придется явным образом заявить о себе, а для этого им понадобится соответствующий протокол. Так как это будет очередной аспект управления IPv6, мы поместим его в удобные рамки ICMPv6 и назовем розыск групповых слушателей (Multicast Listener Discovery, MLD).

    В то же время, источники группового трафика обнаружат себя, передавая пакеты, и дополнительный протокол их розыска не требуется. Групповому маршрутизатору достаточно настроить свои активные сетевые интерфейсы так, чтобы они принимали все групповые пакеты. Например, пока маршрутизатор работает только с IPv6 и Ethernet, ему достаточно принимать все кадры MAC, у которых адрес назначения начинается на 33 33. Такой теневой прием всего группового трафика еще не делает маршрутизатор слушателем всех возможных групп. Групповой маршрутизатор слушает группу только тогда, когда он готов продвигать для нее трафик. Это различие важно, так как настоящий слушатель должен заявить о себе с помощью MLD, а не только втихомолку принимать пакеты.

    Аналог MLD в IPv4 — это протокол IGMP. Его современные версии основаны на точно тех же идеях, а сообщения IGMP играют те же роли, что сообщения MLD. Более того, версии IGMP и MLD фактически синхронизированы, поскольку групповое вещание IPv4 и IPv6 развивается параллельно. Так, IGMPv2 отвечает MLDv1, а IGMPv3 — MLDv2. Что скрывается за этими номерами версий, мы сейчас увидим.

    Работу над MLD мы начнем с того, что уточним, какой объем сведений о слушателях группы Г на канале К действительно необходим маршрутизатору. Будет ли это поименный список? Когда маршрутизатор продвигает групповой пакет в канал К, он передает в этот канал ровно одну копию пакета, а всю остальную работу делают канальные механизмы. Поэтому на самом деле групповому маршрутизатору достаточно знать, есть ли вообще слушатели группы Г на данном канале К, а их число и состав совершенно неважны. Это простое соображение и послужит нашей отправной точкой.

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

    Следующее очевидное соображение состоит в том, что областью распространения MLD будет один канал. Поэтому адресам источника и назначения в сообщениях MLD прекрасно подойдет внутриканальная область. Кроме того, надежность передачи пакетов в пределах канала обычно удовлетворительная, и нам можно не обременять себя и протокол средствами гарантированной доставки данных, а будет достаточно повторять сообщения время от времени.

    Любителям истории TCP/IP будет небезынтересно отметить, что самая ранняя версия IGMP [Приложение I RFC 988], условно известная как IGMPv0, как раз обеспечивала гарантированную доставку сообщений от слушателя к маршрутизатору. На практике оказалось, что без этого можно обойтись и тем упростить реализацию сторон протокола. Ведь гарантированная доставка неизбежно требует хранить историю передачи, тогда как периодический повтор вполне обходится без сохраненного состояния.

    В первом приближении, наша пробная версия MLD могла бы работать следующим образом. Слушатели шлют отчеты (Report) о принимаемых группах, просто перечисляя адреса этих групп. Маршрутизатор вызывает отчеты слушателей, направив в канал явный запрос (Query), хотя слушатель может выслать отчет и по собственной инициативе. Чтобы на первых порах не заботиться о размере отчета в контексте MTU, пусть каждый отчет содержит ровно один групповой адрес. Если слушатель принимает несколько групп, то он вышлет по отдельному отчету о каждой из них. Кто в этой схеме будет задавать ритм? Пока на канале нет группового маршрутизатора, в периодических отчетах смысла тоже нет. Поэтому пусть именно маршрутизатор периодически высылает запрос, а слушатели отвечают на него.

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

    Тем не менее, в одном случае оправдан немедленный и добровольный отчет о группе, а именно когда новый слушатель начинает принимать данную группу. Вот нам еще одно важное действие хоста при вступлении в группу Г на интерфейсе И: направить отчет MLD о группе Г в интерфейс И. Чтобы отчет не потерялся, его стоит повторить несколько раз.

    Какой адрес источника IPv6 будет указан в пакете с отчетом? Если у интерфейса И уже есть внутриканальный адрес, то надо использовать его. Однако на момент вступления в группу Г интерфейс И может быть еще без адресов, например, если группа Г — это группа искомого узла, которая отвечает пробному внутриканальному адресу интерфейса И (см. процедуру DAD в §5.4.1). Выходит, чтобы вступить в группу, нужен адрес, а чтобы назначить адрес, нужна группа? Это типичная "проблема курицы и яйца", для которой у нас уже есть типовое решение: в пакете с отчетом MLD допустим неопределенный адрес источника :: [RFC 3590, §5.2.13 RFC 3810]. Это вполне приемлемо для MLD, поскольку маршрутизатор все равно не ведет поименного списка слушателей группы.

    Все группы искомого узла — внутриканальные. Тогда зачем их вообще объявлять по MLD? Как станет видно через пару абзацев, это весьма резонный вопрос, располагающий к небольшой беседе.

    В такой схеме работы MLD запросу не нужны никакие дополнительные параметры, а отчет содержит всего одно информативное поле, адрес группы. Но как маршрутизатор определит, что у группы Г не осталось слушателей, когда все они прекратят ее прием? В этом случае запрос маршрутизатора не вызовет ни одного отчета об этой группе. Так как запросы и отчеты все же могут теряться, маршрутизатору не следует спешить и надо подождать еще несколько запросов подряд. А уж если и в ответ на них не пришло ни одного отчета, в котором адрес группы равен Г, то значит, ее и правда больше никто не слушает.

    Наконец, нам следует определить, по какому адресу надо слать запрос и отчет. Запрос должен достичь всех узлов канала, так как заранее неизвестно, кто из них групповой слушатель. Поэтому запрос направляется группе "все узлы канала", FF02::1. Чтобы при этом не возникло очередной "проблемы курицы и яйца", пусть эта группа будет особенной: она никогда не объявляется по MLD. Так как она внутриканальная, то и маршрутизации она не подлежит.

    На первый взгляд, можно было бы назначить особый групповой адрес "все слушатели канала". Однако групповое вещание в IPv6 — обязательная функция, которую в любом случае применяет протокол ND (§5), так что выигрыша от такого назначения не было бы вовсе, потому что любой интерфейс IPv6 — член нескольких групп. (Перечислите, каких.)

    Маршрутизатор вправе продвинуть индивидуальный пакет в тот же канал, откуда он пришел, даже если адрес назначения в нем внутриканальный; ведь зонная архитектура IPv6 вполне допускает такое поведение (см. §2.4). В то же время, продвигать обратно в канал групповой пакет точно не следует, потому что это прямая дорога к зацикливанию трафика. А если групповых маршрутизаторов на канале будет несколько, то может даже возникнуть лавинный эффект, когда число копий пакета растет с каждым циклом экспоненциально. Чтобы понизить вероятность такого сбоя, "предельное число шагов" в пакетах MLD устанавливают равным 1, а значит, не применяют к ним GTSM (§5.1).

    Выходит, если трафик внутриканальных групп все равно не подлежит маршрутизации, то и сведения о них маршрутизатору не нужны. Может, их надо вообще исключить из объявлений MLD? На самом деле, нет. Дело здесь в том, что отчеты о внутриканальных группах интересны интеллектуальным коммутаторам ЛВС, которые заняты"подслушиванием" MLD. До этой темы мы доберемся буквально через несколько абзацев.

    Что касается групп из области интерфейса (FFx1:… ), то они имеют значение только в пределах данного интерфейса, а адресованные им пакеты никогда не покидают пределов узла. Поэтому естественно, что группы из этой области никогда не упоминаются в сообщениях MLD. То же справедливо для групп зарезервированной области 0.

    Что касается отчета, то у нас целых три кандидата на адрес его назначения: индивидуальный адрес маршрутизатора, который был источником запроса, группа "все маршрутизаторы канала" и… группа Г, о которой дается отчет. Первый кандидат, то есть адрес маршрутизатора, плох тем, что отчет не получат другие маршрутизаторы того же канала, когда они есть, а слушателям придется отвечать на запрос каждого маршрутизатора отдельно. Маршрутизаторы у нас уже получают запросы друг друга, и они могли бы выбрать, кто из них служит "метрономом" данного канала (генератор запросов, querier), чтобы понизить суммарную нагрузку. Вопрос о точном механизме этих выборов мы пока отложим в наш "внутренний стек", но сама идея оптимизации довольно прозрачна: только один маршрутизатор запрашивает, но отчеты получают все. Второй кандидат, "все маршрутизаторы канала", допускает такую оптимизацию, но вовлекает в прием отчетов маршрутизаторы индивидуального трафика, которым эти отчеты неинтересны. Наконец, группа Г, о которой дается отчет, позволит сообщению достичь всех групповых маршрутизаторов, потому что они уже ведут теневой прием всех возможных групп на всех своих активных интерфейсах; ведь именно так маршрутизаторы следят за источниками группового трафика. Кроме того, отчет получат другие слушатели группы Г, а это может пригодиться для дополнительной оптимизации трафика MLD. Поэтому пусть отчет о группе Г адресуется этой же самой группе — по крайней мере, в нашей самой первой версии MLD.

    Суть дополнительной оптимизации вот в чем. Раз маршрутизатору неинтересен поименный список слушателей, то о каждой группе достаточно одного отчета на весь канал. Пока слушатель задерживает свой отчет о группе Г на случайное время, он заодно ожидает, не придут ли отчеты о Г от других ее слушателей, у которых задержка оказалась короче. Если такой отчет получен, значит, кто-то другой успел отрапортовать о группе Г первым, и повторный отчет избыточен. Как мы скоро увидим, у этого трюка есть и обратная, отрицательная сторона: коммутаторам сложнее следить за топологией группы в пределах ЛВС.

    На схеме, которую мы наметили под видом "пробной версии MLD", основана работа протокола IGMPv1 в IPv4 [Приложение I RFC 1112]. В нем выборы генератора запросов оставлялись вышестоящему протоколу групповой маршрутизации.

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

    Позднее мы увидим, как прием запросов наряду с отчетами позволяет точнее управлять таймерами протокола. Тем не менее, для правильной интерпретации отчета MLD знать текст запроса совершенно не нужно. Более того, отчет MLD может быть и не по запросу, например, когда слушатель начинает прием новой группы.

    Это свойство MLD открывает нам путь к оптимизации группового трафика не только на сетевом, но и на канальном уровне. Скажем, интеллектуальный коммутатор Ethernet вполне может следить за отчетами MLD, которые входят через его порты, и таким образом вести учет слушателей групп на этих портах. Конечно, тогда коммутатору придется нарушить границу между уровнями в стеке протоколов, потому что он будет сначала выделять пакеты IPv6 с отчетами MLD среди всех возможных кадров Ethernet, а затем анализировать их. Последним шагом будет преобразование групповых адресов IPv6 в групповые адреса MAC, так как коммутатор все же управляет трафиком на основании канальных, а не сетевых адресов. Тем не менее, этот трюк вполне по силам современным коммутаторам, вычислительные возможности которых давно превосходят самые смелые мечты пионеров ЭВМ. Поскольку коммутатор в такой схеме подслушивает чужие сообщения, хотя ему это и не положено по его сетевой роли, данный прием известен как подслушивание MLD (MLD snooping).

    Конечно, тот же самый прием коммутаторы используют и по отношению к IPv4, подслушивая IGMP.

    Обратите внимание, какую роль в подслушивании IGMP и MLD играют правила преобразования групповых адресов IP в групповые адреса MAC. Они сводятся к простой подстановке битов (§4.1.1) и потому не представляют трудности для коммутатора. Иначе коммутатор не смог бы воспользоваться информацией из сообщений IGMP или MLD, потому что не знал бы, какой канальный адрес отвечает данной группе IP.

    Хотя с архитектурной точки зрения подслушивание MLD и IGMP — всего лишь сомнительный трюк, на практике оно стало самым популярным подходом к управлению групповым трафиком IP на канальном уровне. Даже общепринятые протоколы вынуждены считаться с ним. В частности, слушатели обязаны объявлять свои внутриканальные группы, кроме FF02::1 и 224.0.0.1, и это явная дань подслушиванию MLD и IGMP.

    Предложите способ управления на канальном уровне группами "все узлы IP", FF02::1 и 224.0.0.1, при том что они не объявляются слушателями. Иными словами, по каким признакам коммутатор может своевременно установить, что на данном порту есть хотя бы один узел IPv4 или IPv6? (Подсказка: ARP с протоколом 0x800 или BOOTP/DHCPv4 — это IPv4; ND или DHCPv6 — это IPv6.)

    Получив работоспособный прототип MLD, мы можем заняться его оптимизацией. Прежде всего, обратим внимание на одну серьезную проблему, которую мы сами же и создали. Когда слушатель группы Г направляет отчет MLD по адресу Г, он рассчитывает, что этот отчет примут и обработают все групповые маршрутизаторы канала, а не только слушатели группы Г. Это различие важно, например, когда группа Г впервые появляется на данном канале и маршрутизаторы о ней еще ничего не знают. Чтобы эта схема сработала, маршрутизаторам будет недостаточно настроить свои канальные фильтры на прием всех групповых пакетов, — им также придется вести анализ всех входящих групповых пакетов, и анализ этот будет более глубоким и трудоемким, чем того требует обычная адресация IPv6. Ведь до сих пор узел, будь то хост или маршрутизатор, заглядывал дальше основного заголовка IPv6 только в том случае, если адрес назначения IPv6 в основном заголовке пакета принадлежал узлу.

    В IPv4 это было, строго говоря, не так, поскольку маршрутизатор просматривал все опции IP.

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

    Обычно, если пакет был индивидуальный, его адрес назначения должен был быть назначен одному из интерфейсов узла; если же пакет был групповой, то узел должен был быть членом данной группы на входном интерфейсе пакета. В противном случае пакет был чужим; хост отбраковывал его, а маршрутизатор продвигал согласно текущей политике. Этот простой критерий "свой/чужой" позволял эффективно выделять пакеты, требующие анализа, из всего трафика, что особенно важно для маршрутизаторов. Теперь же мы фактически обязали групповые маршрутизаторы полностью разбирать всю цепочку заголовков IPv6 в каждом входящем групповом пакете, чтобы выделить из общего потока сообщения ICMPv6, а затем анализировать заголовок ICMPv6, чтобы отделить и обработать интересные маршрутизатору сообщения MLD.

    Вспомним важный факт, который мы узнали из §3.1: в отличие от IPv4, тип полезной нагрузки пакета IPv6 не всегда можно определить прямо из его основного заголовка.

    Простой, но действенный прием против этой проблемы сводится к тому, чтобы заранее помечать пакеты MLD. Делать это должен, конечно же, их источник, но как? Среди всех значений "следующий заголовок" в §3.3.2 мы особо выделили одно, а именно нулевое. Это значение отвечает заголовку пошаговых опций, представляющих интерес для всех узлов по пути пакета. Его особенность в том, что встретиться оно может только в основном заголовке IPv6 ради легкого доступа к пошаговым опциям.

    Почему бы нам не воспользоваться этим механизмом для маркировки служебных пакетов, нарушающих правила адресации? Ведь на самом деле маршрутизаторы IPv6 уже проверяют, нет ли в транзитном пакете пошаговых опций, а стоимость этой проверки низка, потому что ограничена основным заголовком пакета. Просто до сих пор у нас не возникало задач, где бы это пригодилось. Итак, пришло время "сконструировать" пошаговую опцию, которая скажет маршрутизатору: обрати внимание на этот пакет, он может содержать интересные тебе сведения, хотя он и не адресован тебе явным образом. За свою роль эта опция называется "сигнал маршрутизатору" (Router Alert) [RFC 2711].

    Сначала нам надо выбрать численный тип этой опции. Как мы помним из §3.3.2, три старших бита в нем отражают свойства опции. Первое свойство — это можно ли игнорировать опцию, если узел не поддерживает ее. В данном случае это так. Второе свойство — неизменность. У транзитных узлов нет никакой причины изменять значение данной опции, поэтому она будет неизменной. Такой комбинации свойств отвечают три нулевых бита, 000, в старших разрядах типа опции, а значит, значение надо выбрать из диапазона от 0 до 31. Общепринятый тип этой опции — 5.

    Какие данные будет содержать эта опция, и нужны ли они ей вообще? Хотя само ее присутствие — это уже сигнал для маршрутизатора, давайте оптимизируем наш подход и точнее укажем тип сведений, которые маршрутизатор сможет извлечь из данного пакета (см. рис. 8.2). По сути, это классификация протоколов под несколько другим углом зрения. Если протоколы IP (значения для поля "следующий заголовок") делают ударение на инкапсуляции, то сейчас мы перенесем его на способы управления трафиком. Каждый протокол управления получит свой код из соответствующего реестра http://www.iana.org/assignments/ipv6-routeralert-values [ ], и тогда маршрутизатор сможет быстро узнать, интересен ли ему этот пакет, без того чтобы разбирать всю цепочку заголовков. Протокол MLD пришел за своим кодом самым первым и потому получил почетное нулевое значение. Для простоты и определенности пусть все сообщения MLD несут пошаговую опцию "сигнал маршрутизатору" с кодом 0 внутри.

    Чтобы проще объяснить, зачем понадобился еще один реестр протоколов, применим кулинарную параллель: опция "сигнал маршрутизатору" не только выделяет пирожки с начинкой среди обычных булочек, но и говорит, какая там начинка внутри. Ведь гурману важен не способ начинки, а ее вкус.

    Обсудите, как применение опции "сигнал маршрутизатору" влияет на устойчивость и безопасность сети, а также что этому можно противопоставить [§4 RFC 2711].

    (рис 8.2) Пошаговая опция IPv6 "сигнал маршрутизатору"

    Поле кода внутри опции "сигнал маршрутизатору" — длиной 2 байта. Как читатель уточнит его спецификацию, чтобы она стала однозначной? (Ответ: способ кодирования целый беззнаковый, порядок байтов сетевой.)

    Упражнение для читателя: составить заголовок пошаговых опций, содержащий только опцию "сигнал маршрутизатору" с кодом MLD, и заполнить все поля. Сможете ли вы это сделать? Если нет, то как обойти возникшее затруднение? (Ответ: придется добавить два Pad1 или один пустой PadN.)

    Перейдем к следующему усовершенствованию. В предварительной версии MLD мы отложили вопрос о том, как групповые маршрутизаторы канала выберут ровно одного из своего числа на роль генератора запросов ( querier). Вот простой и остроумный подход к этой, казалось бы, сложной задаче: пускай роль генератора играет маршрутизатор с наименьшим адресом IP.

    Мы, конечно же, помним из §2.2, что адрес IP (v4 или v6) — это цепочка битов с определенным порядком старшинства, а значит, ее можно рассматривать как двоичную запись целого неотрицательного числа. Однозначность позиционной записи чисел гарантирует нам, что соответствие между побитовым представлением адреса и его численным значением взаимно однозначно, а значит, у разных адресов заведомо разное численное значение. Точнее говоря, позиционная запись однозначна с точностью до незначащих нулей, но адресов IP эта оговорка не касается ввиду их фиксированной разрядности. Если бы адреса IP были переменной длины, нам пришлось бы явным образом уточнить, имеют ли значение нули в старших разрядах адреса, например, являются ли 1 и 01 суть разными адресами или же эквивалентными представлениями одного и того же адреса. Разумеется, все эти соображения справедливы только в пределах определенной версии IP.

    Чуть выше мы позаботились, чтобы маршрутизаторы получали запросы друг друга. Каждый из них шлет свои запросы с определенного внутриканального адреса, который, несомненно, уникален в пределах канала — об этом позаботился механизм DAD из §5.4.1. Теперь представим себе, что маршрутизатор получил запрос, адрес IP источника в котором численно меньше, чем его собственный. Это значит, что есть более вероятный претендент на роль генератора, и собственные запросы надо приостановить на какое-то время, большее, чем стандартный период повтора запросов. В результате самый подходящий кандидат станет непрерывно подавлять своими запросами другие маршрутизаторы, и те будут оставаться пассивными наблюдателями (non-querier). Математическая основа этого трюка — транзитивность отношений "больше" и "меньше" на множестве целых чисел: $$A>B, B>C\Rightarrow A>C$$. Благодаря этому свойству процесс выборов гарантированно сойдется, как только все маршрутизаторы получат все неподавленные запросы своих соперников. Когда же текущий генератор выйдет из игры, сработают таймеры, процедура выборов повторится, и место генератора займет новый победитель. В результате мы получаем простой и устойчивый механизм.

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

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

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

    Чтобы ускорить этот процесс, пусть слушатель явным образом извещает маршрутизаторы о прекращении приема данной группы Г. Для этого он высылает сообщение нового типа, итог (Done). Как и отчет, это сообщение содержит в себе адрес группы Г, о которой идет речь. По какому адресу лучше всего направить итог? Здесь у нас пока всего два варианта, "все маршрутизаторы канала", FF02::2, и адрес данной группы Г. Обычно маршрутизаторов на канале меньше, чем потенциальных слушателей группы, а сообщения типа "итог" будут относительно редки, так что мы остановим свой выбор на первом варианте, FF02::2.

    Теперь представим себе, что групповые маршрутизаторы канала получили итоговое сообщение о группе Г от некоего слушателя. Поскольку маршрутизаторы не ведут поименный список слушателей и даже не считают их "по головам", то само по себе это сообщение еще не дает им всей необходимой информации — оно только извещает, что произошли изменения в числе слушателей группы Г. Естественной реакцией маршрутизаторов будет проверить, остались ли в группе Г еще слушатели. Для этого генератору запросов надо послать в канал несколько запросов с короткими паузами между ними.

    Если это будут обычные запросы, они вызовут целый поток информации, не относящейся к делу. Поэтому нам следует ограничить эти запросы группой Г, о которой пришло итоговое сообщение. Во-первых, такие запросы надо направлять по адресу группы Г, а не "все узлы канала". Во-вторых, в самих запросах надо указать, что они касаются только группы Г. То есть тело запроса тоже должно включать в себя поле "адрес группы". В обычном, общем запросе (General Query) значение этого поля будет нулевым, тогда как в запросе, ограниченном группой (Multicast-Address-Specific Query), это поле содержит действительный адрес группы.

    Мелкая, но важная деталь — это взаимодействие между процедурой быстрой проверки группы и выборами генератора запросов. Как быть текущему генератору, если он еще не послал положенное число запросов группе Г, а тем временем пришел чужой запрос с еще меньшим адресом источника? Пусть ради простоты и устойчивости протокола текущий генератор всегда доводит процедуру быстрой проверки до конца. Ведь выборы генератора — это лишь оптимизация, и не будет никакой беды в том, что на протяжении нескольких секунд запросы будут слать два маршрутизатора.

    Последний штрих в нашей схеме быстрой проверки группы коснется координации генератора запросов с наблюдателями. Как последние узнают, что идет оперативная проверка группы и связанные с ней таймеры надо установить на более короткий интервал? По нашему плану, наблюдатели продолжают получать запросы, а ритм задает генератор запросов, так что пускай он заодно сообщает наблюдателям, какой тайм-аут надо связать с данным запросом. Для этого достаточно одного целочисленного поля в формате запроса. В свою очередь, слушателям оно пригодится, чтобы равномернее распределить отчеты в доступном времени, выбирая случайную задержку от нуля до значения этого поля. За эту роль данное поле известно как MRD (Maximum Response Delay, максимальная задержка отклика). Размерность значения MRD — миллисекунды.

    Маршрутизаторы обращают внимание на поле MRD, только если принят запрос, ограниченный группой. Общие запросы на интервалы таймеров не влияют.

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

    Перейдем к формату сообщений. Формат запроса и ответа будет одним и тем же, чтобы упростить реализацию (см. рис. 8.3). Конечно, поле(рис 8.3) Сообщение MLDv1

    "Сконструированные" нами механизмы составляют костяк первой версии протокола розыска слушателей IPv6, MLDv1 [RFC 2710].

    Тем же самым образом работает IGMPv2 в IPv4 [RFC 2236].

    Любопытно отметить, что у сообщений MLDv1 типы численно меньше, чем у сообщений ND. Это можно объяснить тем, что MLD логически предшествует ND. К примеру, группу искомого узла надо объявить по MLD до того, как ею можно пользоваться в целях ND.

    Мы провели довольно много времени, пересматривая и оттачивая детали протокола MLDv1, так что давайте соберем самые главные его черты в одной таблице, Табл. 8.1, а заодно сравним его с нашей пробной "нулевой версией", аналогичной IGMPv1 в IPv4, чтобы лучше увидеть проделанный путь.

    Сравнение возможностей "MLDv0" и MLDv1
    "MLDv0" MLDv1
    Типы сообщений ICMPv6 "запрос MLD" (130), "отчет MLD" (131) "запрос MLD" (130), "отчет MLD" (131), "итог MLD" (132)
    Виды запроса Только общий Общий и ограниченный группой
    Форма отчета Одна группа на сообщение Одна группа на сообщение
    Адрес назначения в отчете Объявляемая группа Объявляемая группа
    Выборы генератора запросов Механизм не определен По адресу IPv6
    Подавление избыточных отчетов Да Да
    Управление интервалом таймера Нет Да

    Способны ли мы еще улучшить наш результат во второй версии протокола, MLDv2 [RFC 3810]? Конечно! Мы даже готовы немедленно выступить с черновым списком усовершенствований:

  • Центральным объектом MLDv1 выступает группа. В частности, сообщения MLDv1 относятся к одной группе (или ко всем группам сразу), а механизм подавления избыточных отчетов достигает того, что о каждой группе дается один отчет. Следовательно, накладные расходы на работу такого протокола растут пропорционально числу активных групп, а оно может быть велико, особенно если на канале в основном расположены маршрутизаторы, каждый из которых продвигает трафик многих разных групп. Напротив, число слушателей на канале вряд ли будет очень большим из чисто практических соображений. Поэтому имеет смысл перенести фокус протокола с группы на слушателя. Для начала надо переработать формат отчета так, чтобы он смог нести информацию о многих группах сразу.
  • MLDv1 поддерживает только один режим группового вещания, а именно ASM. В новой версии MLD совершенно необходима поддержка SSM и SFM. О различиях между этими режимами см. §2.9.
  • В MLDv1 механизм координации генератора запросов с наблюдателями находится в зачаточном состоянии. Его надо распространить на прочие важные параметры протокола: интервал между запросами, число повторов.
  • Аналогичные усовершенствования вошли в IGMPv3 для IPv4 [RFC 3376].

    Первый пункт мы осуществим, усложнив формат отчета (см. рис. 8.4). Теперь он будет содержать переменное число записей о группах. Однако тогда нам придется пересмотреть адрес назначения отчета MLDv2. В MLDv1 это был адрес группы, о которой дается отчет, поскольку он был ровно один. Когда отчет дается о переменном числе групп, нам ничего не остается, как адресовать его фиксированной общепринятой группе "все маршрутизаторы с поддержкой MLDv2", FF02::16. Другие слушатели больше не получат этот отчет, а значит, механизм подавления избыточных отчетов работать не сможет. Его ценность уже упала благодаря понижению числа отчетов, так что в MLDv2 мы от него полностью откажемся. Пусть каждый слушатель MLDv2 говорит сам за себя, без оглядки на других. Это заодно упростит реализацию слушателя.

    (рис 8.4) Отчет MLDv2

    Еще один аргумент против подавления избыточных отчетов в MLDv2 связан с подслушиванием MLD, которое ведут интеллектуальные коммутаторы ЛВС [§A.2 RFC 3810]. В простейшем случае такой коммутатор продвигает групповые кадры только в те порты, откуда были замечены отчеты MLD о данной группе. Если же отчет подавлен, то коммутатор решит, что на этом порту слушателей нет, и продвигать групповые кадры туда не станет. Чтобы обойти эту проблему, коммутатор, в свою очередь, подавляет продвижение отчетов MLD в те порты, где, как ему кажется, нет групповых маршрутизаторов [§2.1.1 RFC 4541]. То же справедливо для IGMP. Не стоит сомневаться, что такой трюк только усложняет настройку сети и дестабилизирует ее работу.

    Родственная проблема состоит в том, что коммутатор должен копировать весь групповой трафик в порты, где есть групповые маршрутизаторы, поскольку те ведут теневой прием всех групп, не вступая в них явным образом. В противном случае, например, маршрутизаторы не смогут получать отчеты MLDv1. Увы, коммутатор не может надежно обнаружить групповые маршрутизаторы по их запросам MLD, так как только генератор запросов ведет их постоянную передачу, а наблюдатели молча следят за чужими сообщениями MLD. Поэтому, чтобы коммутатор мог самостоятельно определить, на каких портах есть групповые маршрутизаторы, предложен отдельный простой протокол MRD (Multicast Router Discovery) [RFC 4286].

    Объединение информации о многих группах в один отчет может привести к тому, что полная длина пакета с отчетом превысит MTU канала, а фрагментировать отчет было бы нежелательно. Чтобы обойти и эту трудность, один длинный отчет о множестве групп будет эквивалентен нескольким отчетам поменьше, каждый о подмножестве этого множества, вплоть до одного отчета на группу. Это позволит слушателю разбить длинный список групп на порции подходящего размера, не применяя никаких дополнительных механизмов, а просто управляя числом групп на отчет [§5.2.15 RFC 3810].

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

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

    Для начала заметим, что для маршрутизатора нет принципиальной разницы между SSM и SFM. Это хост знает, что в режиме SSM разные источники S в комбинированном адресе (S,G) означают разные каналы вещания, которые могут принимать совершенно разные приложения, как мы обсудили в §2.9. Маршрутизатор же просто оптимизирует распространение группового трафика. Поэтому, когда хост принимает два канала, $$(S_1,G) и (S_2,G)$$, которые отличаются только адресом источника, но не адресом назначения, маршрутизатору достаточно допустить источники $$S_1 b S_2$$ к группе G, а остальные блокировать, чтобы не расходовать впустую ресурсы сети. То же самый подход применим и к SFM.

    Остаточные особенности фильтрации SSM обсуждаются в [RFC 4604].

    В общем, картина такая: маршрутизатор допускает некое множество адресов источника S к данной группе G, а остальные адреса блокирует. Поэтому нашу модель фильтрации мы выразим на языке элементарной теории множеств, а оперировать она будет только адресами источника, так как у каждого группового адреса назначения будет свой независимый фильтр. Множество разрешенных адресов S мы станем называть первичным, потому что в нашей модели оно составляет истинный смысл фильтра, хотя способы фиксации (кодирования) этой информации могут быть разными.

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

    Множество всех адресов IPv6 — это универсум данной задачи, то есть множество всех возможных значений. Оно конечно, и поэтому теоретически любой фильтр можно записать, просто перечислив адреса источника. Так, режим вещания ASM обеспечивает фильтр, содержащий все адреса IPv6, а полное прекращение приема группы равноценно фильтру, в котором нет ни одного адреса (пустое множество). А когда несколько слушателей выражают свои пожелания касательно фильтрации трафика в виде множеств, маршрутизатору достаточно найти их объединение, чтобы удовлетворить все запросы и не заблокировать ни один желательный источник: S=\bigcup_{i} S_i. На бумаге выглядит неплохо!

    Как следствие, в MLDv2 можно вообще отказаться от сообщений типа "итог" и сигнализировать о выходе из группы на языке множеств. Мы сознательно не пытаемся исключить недопустимые адреса источника, такие как групповые адреса, из первичного фильтра, поскольку наша работа ведется в предположении, что входящий пакет с недопустимым адресом источника будет отбракован на самой ранней стадии его обработки. Это позволяет упростить наши теоретико-множественные выкладки и сделать их более прозрачными.

    Но как нам быть со случаем, когда слушатель SFM на самом деле хочет заблокировать несколько источников, не препятствуя остальным? Этому отвечает первичное множество, равное дополнению множества блокируемых адресов . То есть разности множества всех адресов и множества блокируемых адресов. Нетрудно подсчитать, что при блокировании N адресов в первичном фильтре остается $$2^128-N$$ элементов, и при небольшом N это будет астрономическое число! Если множество всех адресов для режима ASM (N = 0) еще можно было бы кодировать кратко, как особый случай, режим SFM с блокированием потребует явного перечисления этого фантастического количества адресов, потому что мы заранее не знаем, какие источники предпочтет исключить слушатель. Очевидно, что ни передавать, ни хранить такое множество невозможно. Поэтому настало время нам перейти от красивой теории к практическим хитростям и трюкам.

    Главный наш трюк мы позаимствуем из сценария с блокированием источников. Пока степень наполнения фильтра адресами близка к одной из двух крайностей, все или ни одного, состояние фильтра можно записать довольно кратко. Когда наш первичный фильтр содержит всего несколько адресов, мы перечисляем именно их. Когда же первичный фильтр включает в себя почти все возможные адреса, то достаточно перечислить множество недостающих адресов $$\bar{S}$$, потому что оно связано с первичным множеством фильтра однозначным образом: $$\bar{S}=\S$$, где означает дополнение множества S до универсума (множества всех адресов IPv6).

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

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

    Со своей стороны, слушатель тоже фильтрует трафик группы в одном из этих двух режимов, потому что его касаются те же самые практические соображения насчет размера фильтра. И тогда поддержка SFM в MLDv2 сводится к следующему: во-первых, передать информацию о фильтрах от слушателей к маршрутизатору, а во-вторых, свести вместе информацию от разных слушателей, чтобы объединенный фильтр маршрутизатора удовлетворил их всех.

    В такой модели группового фильтра режим приема ASM (прием от любого источника) — это подмножество SFM и выражается фильтром EXCLUDE($$\varnothing$$), где ($$\varnothing$$ — пустое множество. Говоря по-русски, ASM — это прием от всех источников без исключения (подразумевая, что технически такое исключение возможно).

    Чтобы передавать такого рода информацию, каждая запись в отчете MLDv2 (см. рис 8.5(рис 8.5) Запись о группе в отчете MLDv2

    Чтобы провести начальную загрузку своего фильтра на маршрутизаторы, слушатель должен дословно перечислить адреса источника в фильтре и указать его режим работы. Для этой цели достаточно двух типов записи, CHANGE_TO_INCLUDE_MODE (тип 3) и CHANGE_TO_EXCLUDE_MODE (тип 4). Первый из них говорит, что слушатель перевел свой фильтр во включающий режим и загрузил в него приведенный список источников. Второй тип сообщает то же самое для фильтра в исключающем режиме. Чтобы нам было удобнее обсуждать теоретико-множественные свойства типов записей, мы станем их кратко записывать как TO_IN(S) и TO_EX(S) , соответственно, где S — множество источников, перечисленное в данной записи.

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

    В первом из них слушатель хочет разрешить или блокировать несколько дополнительных источников, не меняя остальной их список. Так возникают типы записи ALLOW_NEW_SOURCES (тип 5) и BLOCK_OLD_SOURCES (тип 6), сокращенно ALLOW(S) и BLOCK(S).

    Второй заслуживающий оптимизации случай — это периодический ответ на запрос маршрутизатора, которым слушатель сообщает свой текущий фильтр, хотя тот остается без изменений. Для этого он использует отдельные типы записи, MODE_IS_INCLUDE (тип 1) и MODE_IS_EXCLUDE (тип 2), сокращенно IS_IN(S) и IS_EX(S) . Благодаря этому маршрутизаторы могут оптимизировать обработку фильтров, пока те остаются постоянными. Кроме того, маршрутизатор не должен реагировать на эти записи дополнительными запросами, чтобы не вызвать зацикливание протокола.

    Чтобы не повторять все время "запись отчета" и не путать ее с записью о группе в оперативной памяти узла, впредь мы будем говорить просто "отчет", например, "отчет IS_EX", памятуя при этом, что соответствующее сообщение MLDv2 составное и может содержать в себе более одного отчета, каждый из них об определенной группе.

    Слушатель вполне может комбинировать разные отчеты об одной и той же группе, чтобы гибко уведомлять маршрутизаторы об изменениях в состоянии своего фильтра данной группы, используя при этом минимум сообщений MLDv2. К примеру, когда фильтр группы Г переходит из состояния INCLUDE( ) в состояние INCLUDE( ), где $$A$$ и $$B$$ — вообще говоря, разные множества источников, допущенных к данной группе Г, можно выделить два подмножества источников, затронутых этой операцией. Множество $$A$$ за вычетом множества $$B$$, в математической нотации $$A\B$$ больше не интересно слушателю, и его можно заблокировать. Напротив, множество $$B$$, за вычетом множества $$A$$ , то есть $$B\A$$, раньше не интересовало слушателя, так что его надо разблокировать и допустить к группе. В то же время, пересечение множеств $$A$$ и $$B$$, то есть $$A\cap B$$, остается интересным слушателю, и его статус не изменяется. Поэтому, чтобы объявить о данном переходе, достаточно два отчета в любом порядке: BLOCK( ) и ALLOW( ). Эти отчеты можно объединить в одно сообщение MLDv2, если позволяет их длина.

    Пусть читатель наглядно изобразит этот пример с помощью кругов Эйлера.

    В принципе, то же самое изменение можно было бы выразить всего одним отчетом TO_IN(B), однако этот отчет подразумевает, что изменился режим работы фильтра. В ответ маршрутизаторы начнут дополнительные проверки. Кроме того, если фильтр сам по себе длинный, а изменился он всего на пару источников, то суммарная длина двух отчетов ALLOW и BLOCK будет меньше, чем длина одного TO_IN, так как ALLOW и BLOCK передают только изменения, а TO_IN и TO_EX — фильтр целиком. Поэтому слушателю будет лучше воздержаться от применения TO_IN или TO_EX, когда подстраивается уже существующий фильтр.

    Используя наши соображения, составим сводку основных правил для слушателя группы в Табл. 8.2. Здесь $$A\backslash B$$ обозначает разность множеств и (множество всех элементов , которые не принадлежат также ), а ($$\varnothing$$ — пустое множество.

    Правила отчетности для слушателя MLDv2
    Текущее состояние Новое состояние Отчет
    начало приема или EXCLUDE( $$A$$) INCLUDE($$B$$ ) TO_IN( $$B$$)
    начало приема или INCLUDE($$A$$ ) EXCLUDE($$B$$ ) TO_EX($$B$$ )
    INCLUDE($$A$$ ) INCLUDE($$B$$ ) BLOCK($$A\backslash B$$), ALLOW($$B\backslash A$$)
    EXCLUDE($$A$$ ) EXCLUDE($$B$$ ) ALLOW($$A\backslash B$$), BLOCK($$B\backslash A$$)
    INCLUDE($$A$$ ) или EXCLUDE($$A$$ ) конец приема TO_IN($$\varnothing$$)

    На практике дополнительная сложность возникает из-за того, что источник должен повторить свой отчет несколько раз. Если группа изменит свое состояние во время повтора отчета о ней, надо объединить старый и новый отчет, используя все те же соображения теории множеств [§6.1 RFC 3810].

    Очевидная особенность SSM состоит в том, что о групповых адресах FF3x::/96 не должно быть отчетов IS_EX или TO_EX. Дело в том, что слушатель SSM заведомо заинтересован в ограниченном числе источников, и поэтому он может вести прием группы только в режиме INCLUDE [§2.2.2 RFC 4604].

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

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

    $$R=\bigcup_{i}I_i$$ .

    Если же все источники работают в исключающем режиме, то лучшее, что может сделать маршрутизатор, это блокировать пересечение всех исключающих фильтров :

    $$\bar R=\bigcap_{i}E_i$$.

    Ведь для множеств справедливо такое следствие законов де Моргана:

    $$(A\backslash B) \cup (A\backslash C)=A\backslash (B\cap C)$$.

    Если A — это множество всех адресов IPv6, а B и C — множества двух исключающих фильтров, то смысл этого равенства именно в том, что пересечение исключающих фильтров эквивалентно объединению включающих фильтров.

    А как быть, когда на канале есть слушатели группы в разных режимах? Снова применим правило объединения первичных множеств для создания удовлетворительного фильтра. При объединении множеств число элементов не уменьшается, а первичное множество любого исключающего фильтра, пока он разумной длины, уже содержит почти все адреса IPv6. Значит, и суммарный фильтр в своем первичном виде будет содержать почти $$2^{128}$$ элементов. Как мы знаем, в этом случае надо просто блокировать остальные адреса, чтобы не иметь дела с фильтром чудовищной длины. Поэтому подходящим режимом суммарного фильтра будет именно исключающий. Иными словами, как только хотя бы один из слушателей переходит в исключающий режим, то же самое вынуждены сделать и маршрутизаторы. При этом блокировать надо пересечение всех исключающих фильтров за вычетом объединения всех включающих фильтров:

    $$\bar R=\bigcap_{i}E_i \backslash \bigcup_{i}I_i $$.

    В частности, пока есть слушатели в режиме ASM$$\exists n: E_n=\varnothing$$, этот фильтр будет пустой и маршрутизатор не сможет блокировать ни один источник. Когда же последний слушатель в исключающем режиме прекратит прием группы, маршрутизатору следует вернуться во включающий режим, чтобы эффективнее фильтровать трафик группы. Если затем и слушатели во включающем режиме завершат прием, включающий фильтр опустеет: $$R=\varnothing $$, — что отвечает концу продвижения группы в данный канал.

    Это и есть теоретико-множественная основа поддержки SFM и SSM в MLDv2. Все остальное в ней — суть важные, но не несущие фундаментального значения детали. Среди них выделяется вопрос о том, как маршрутизатор станет управлять текущей информацией о группе, чтобы она оставалась актуальной. В MLDv1 для этого было достаточно связать с группой один таймер, а когда кто-то из слушателей прекращал прием группы, запросом о данной группе проверить, остались ли в ней другие слушатели. То есть элементарным объектом управления выступала группа. Теперь же разные слушатели изъявляют желание, или нежелание, принимать трафик группы из разных источников, но маршрутизатор по-прежнему не ведет поименного списка слушателей и не запоминает, кто именно из них заинтересован в данном источнике. Поэтому отдельный таймер потребуется каждой записи об источнике группы в памяти маршрутизатора [§7.2 RFC 3810], а элементарным объектом управления в MLDv2 станет источник данной группы, как это показано на рис 8.6(рис 8.6) Структуры данных группового маршрутизатора - модель MLDv2

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

    Когда слушатель группы заявляет отчетом IS_IN(A), TO_IN(A) или ALLOW(A) о своем желании принимать пакеты из некоторого множества источников A, маршрутизатор обязан немедленно допустить трафик из источников A в канал. Напротив, когда слушатель группы не желает вести прием из данного множества источников B и включает в свой отчет элемент IS_EX(B), TO_EX(B) или BLOCK(B) , маршрутизатор не вправе тут же заблокировать все множество источников B, потому что, возможно, у некоторых источников в нем есть и другие слушатели. Точно так же в MLDv1 маршрутизатор не мог прекратить продвижение трафика группы, едва получив итоговое сообщение от одного слушателя, — он был обязан проверить, остались ли у группы другие слушатели. Это вызвано тем, что маршрутизатор не ведет поименного учета слушателей.

    Тем не менее, по приходу отчета IS_EX(B), TO_EX(B) или BLOCK(B) маршрутизатору не обязательно проверять все множество блокируемых источников B. Ведь в этот момент у маршрутизатора уже есть определенное первичное множество допущенных источников S, закодированное в терминах INCLUDE или EXCLUDE . (Как мы говорили, во включающем режиме (INCLUDE ) первичное множество просто равно фильтру, который маршрутизатор хранит в своей памяти: $$S=R$$ , — а в исключающем режиме (EXCLUDE ) оно равно дополнению этого фильтра до множества всех адресов IPv6, нашего универсума: $$S=\bar R$$.) Поэтому проверить запросом необходимо только пересечение множеств B и S: $$Q=B\cap S$$, — так как адреса вне этого подмножества в любом случае сохраняют свое состояние: подмножество $$S\backslash B$$ по-прежнему остается допущенным к группе, а $$B\backslash S$$ уже заблокировано.

    Чтобы провести проверку источников направленно, маршрутизатору понадобится уточнить запрос MLD не только группой, о которой идет речь, но и множеством источников Q, состояние которых его интересует. Поэтому в MLDv2 у запроса появляется новая разновидность: запрос, ограниченный группой и источниками ( Multicast Address and Source Specific Query ). Нужно ли уточнять в таком запросе режим фильтрации, INCLUDE или EXCLUDE ? На самом деле, групповой маршрутизатор всегда спрашивает только о желательных источниках — он никогда не ставит вопрос: "Кто блокирует данный источник?" — потому что блокировка происходит по умолчанию. Это следует из самого предназначения MLD: избавиться от как можно большей доли ненужного группового трафика. Множество же $$Q=B\cap S$$ содержит не больше элементов, чем множество B, приведенное в отчете. Поэтому множество Q тоже можно указать простым перечислением его элементов. Следовательно, запрос MLDv2 ставится только в режиме INCLUDE , и явное указание режима в нем излишне. Такой запрос о группе Г, ограниченный списком источников Q, мы будем обозначать Q(Г,Q), а запрос, ограниченный только группой Г — просто Q(Г). Запрос Q(Г) отличается от Q(Г,Q) тем, что содержит ноль источников, то есть список Q в нем пуст. Их общий формат показан на рис. 8.7 и содержит несколько полей, которые понадобятся нам чуть позднее.

    (рис 8.7) Запрос MLDv2

    Хотя ограниченность запроса Q(Г,Q) режимом INCLUDE вполне обоснована, она приходит в противоречие с исключающим режимом работы фильтра и вызывает дополнительную сложность в управлении проверкой источников. Пока фильтр самого маршрутизатора работает во включающем режиме, отслеживать состояние проверки множества источников Q просто: Q — это заведомо подмножество фильтра $$(B\cap S\subseteq S)$$, о каждом элементе Q уже есть запись в памяти маршрутизатора, и потому при проверке достаточно понизить тайм-ауты на всех записях Q. Напротив, в исключающем режиме элементы Q заведомо отсутствуют в фильтре$$(B\cap S\subseteq \nsubseteq \backslash S)$$ , и маршрутизатору придется хранить записи об элементах Q отдельно от рабочего фильтра.

    Поэтому во включающем режиме состояние группы, с точки зрения маршрутизатора, можно задать всего одним списком источников, тогда как в исключающем режиме понадобятся целых два списка. Общепринятые обозначения этих состояний [§2.3 RFC 3810] приведены и прокомментированы в Табл. 8.3.

    Режимы фильтра группы в маршрутизаторе MLDv2
    Обозначение Режим Смысл параметров
    INCLUDE($$A$$) Включающий $$A$$ — "список включений" (Include List). Это список источников, допущенных к группе. Все прочие источники заблокированы.
    EXCLUDE($$X$$,$$Y$$) Исключающий X — "список требований" (Requested List). Это список источников, которые все еще могут быть интересны некоторым слушателям. Поэтому их нельзя немедленно блокировать, а необходимо проверить с помощью запроса MLD. Ниже мы встретим еще одну роль списка требований. Y — "список исключений" (Exclude List). Это список заблокированных источников. Все прочие источники, включая элементы X, допущены к группе.

    В состоянии EXCLUDE(X,Y) пересечение множеств X и Y, очевидно, пусто: $$X\cap Y= \varnothing$$, — поскольку нельзя одновременно допускать и блокировать один и тот же источник. Следовательно, X вообще не влияет на работу фильтра в текущий момент. Но если на записи об элементе $$x \in X$$ истечет тайм-аут, в модели MLD это будет означать, что данный источник x больше не интересен никому. Тогда маршрутизатор сможет перенести элемент x из списка X в список Y, тем самым заблокировав источник x и избавив канал от ненужного группового трафика. В этом и состоит роль списка X.

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

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

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

    Конечно же, мы помним, что запись об источнике привязана к определенной группе. Например, один и тот же источник И может быть допущен маршрутизатором к группе Г1, но заблокирован для группы Г2. В терминах заголовка IPv6 это означает, что пакеты от И к Г1 будут продвигаться в данный канал, а пакеты от И к Г2 — нет. Это находит прямое отражение в иерархии структур, которые маршрутизатор хранит в своей памяти.

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

    С другой стороны, список исключений — атрибут исключающего режима фильтра, а в этом режиме маршрутизатор и близко не может подойти к своей заветной мечте, а именно заблокировать все источники, потому что ему дозволено блокировать только перечисленные N из них, а остальные $$2^{128}-N$$ беспрепятственно шлют пакеты группе. Это настоящий кошмар для маршрутизатора! Поэтому в его интересах перевести фильтр группы во включающий режим, как только исчезнут слушатели в исключающем режиме. Маршрутизатор не ведет их поименного списка, и ему остается снова положиться на таймер. Это так называемый "таймер фильтра" (Filter Timer) на записи о группе. Он активен только в исключающем режиме и перезапускается по приходу отчета IS_EX или TO_EX, поскольку такой отчет говорит, что на канале все еще есть слушатели группы в исключающем режиме. Но если интервал этого таймера истечет, у маршрутизатора будет полное право переключить фильтр во включающий режим за отсутствием слушателей группы в исключающем режиме.

    Однако, как маршрутизатор ни стремится свести трафик группы на нет, он обязан допускать источники к группе по требованию ее слушателей. В частности, ему не пристало самовольно блокировать те источники, о которых недавно были отчеты IS_IN, TO_IN или ALLOW. Поэтому, переводя фильтр группы во включающий режим, маршрутизатор обязан позаботиться, чтобы новый список включений заранее содержал в себе, по меньшей мере, все записи о желательных источниках; ведь иначе они окажутся заблокированы по умолчанию. Выходит, что накапливать эту информацию фильтр должен заранее, еще в исключающем режиме. Понадобится ли для этого новый список источников в памяти маршрутизатора? Попробуем обойтись уже доступным списком требований, если он сможет играть обе роли. В принципе, маршрутизатору ничто не мешает сохранять записи о желательных источниках в этом же списке. Вопрос только в том, насколько оправдано хранить оба вида информации в одном списке: записи о желательных источниках и записи о тех источниках, которые маршрутизатор пытается заблокировать. Чтобы предотвратить путаницу, заметим такое простое свойство "собираемого" нами механизма: если пришел отчет IS_EX или TO_EX, то фильтр останется в исключающем режиме как минимум на протяжении интервала своего таймера. Если этот интервал будет, скажем, равен тайм-ауту записи об источнике, то у слушателей во включающем режиме будет еще достаточно времени, чтобы снова заявить о своем выборе источников в ответ на периодические запросы маршрутизатора. Поэтому маршрутизатор, получив отчет IS_EX или TO_EX, вправе вычистить из списка требований все записи, кроме текущих кандидатов на блокировку [§7.2.3 RFC 3810].

    Выше мы позволили себе вольность говорить о работе таймеров маршрутизатора в обыденных терминах "давно" и "недавно". В общем, понятно, что "недавно" — это пока интервал таймера не истек, а какой таймер, следует из контекста. Тем не менее, нам явно следует добавить определенности в эту картину. Мы сошлись на том, что у каждой группы есть два вида таймеров.

    Первый из них — это таймер одного источника данной группы. Во включающем режиме MLDv2 он играет ту же роль, что и таймер всей группы в MLDv1, а именно позволяет маршрутизатору определить, что пора блокировать трафик, так как его больше никто не принимает. Разница только в том, что маршрутизатор MLDv1 оперировал целыми группами, тогда как его "потомок" в MLDv2 работает тоньше и управляет отдельными источниками, вещающими группе. Поэтому, когда срабатывает такой таймер, в MLDv1 маршрутизатор удалил бы запись о группе, а в MLDv2 он удаляет вместо этого запись об одном источнике. Когда фильтр INCLUDE(A) опустеет, будет пора удалить и всю запись о группе, так что отельный таймер группы здесь не требуется.

    Насколько большой интервал понадобится таймеру источника? Этот таймер перезапускается по приходу отчета, что данный источник интересен какому-то слушателю данной группы, и поэтому интервал должен быть таким, чтобы заведомо перекрыть период между отчетами; ведь иначе состояние фильтра группы будет неустойчивым, фильтр станет "хлопать". Пока слушатели не меняют своего состояния, их отчеты вызывает маршрутизатор, высылая периодические запросы. Это довольно дорогая операция, так как, в теории, все слушатели канала должны в ответ сообщить свои фильтры в отчетах IS_IN или IS_EX, поэтому период между запросами, QI (Query Interval), должен быть довольно большим, по умолчанию 125 секунд [§9.2 RFC 3810].

    Вдобавок одиночный запрос мог не дойти до некоторых слушателей, скажем, из-за сбоя или перегрузки канала. Тем не менее, есть надежда, что какой-то из последующих запросов все-таки дойдет, и поэтому маршрутизатор должен подождать, как минимум, еще несколько интервалов QI, прежде чем объявить источник невостребованным и удалить его запись. Сколько всего раз надо передать сообщение, чтобы оно наверняка дошло, — это параметр RV (Robustness Variable), зависящий от ненадежности канала, по умолчанию 2 [§9.2 RFC 3810]. Такой повтор защитит от $$RV-1$$ потерь. Наконец, слушатели не ответят на запрос все хором, а сделают случайные паузы, чтобы распределить нагрузку на канал. Верхняя граница такой паузы — параметр QRI (Query Response Interval), по умолчанию 10 секунд [§9.3 RFC 3810].

    В результате, пока сеть стабильна, интервал таймера источника должен быть не менее чем $$QI \times RV+QRI$$; по умолчанию выходит 260 секунд. Этот интервал обозначают MALI (Multicast Address Listening Interval) [§9.4 RFC 3810].

    Интервал QI входит в MALI ровно RV раз, а не RV - 1, и вот почему. Нам никто не гарантирует, что возникновение записи об источнике совпадает с отправкой периодического запроса. Поэтому до ближайшего запроса придется подождать, самое большее, время QI. Затем следует RV - 1 интервалов длиной QI, когда запрос повторяется. Поэтому всего выходит RV интервалов длины QI.

    Чем пренебрегает формула для MALI? (Задержкой канала.)

    Но вот, представим себе, маршрутизатор получает отчет, который дает ему повод усомниться в некоторых записях, к примеру, отчет BLOCK. В ответ маршрутизатор начинает активную проверку этих записей запросами, ограниченными группой и списком источников. Эквивалентом этого сценария в MLDv1 был приход сообщения "итог". Чтобы справиться с проверкой поскорее, не жертвуя при этом достоверностью результатов, маршрутизатор передает запрос LLQC раз (Last Listener Query Count, по умолчанию равен RV [§9.9 RFC 3810]), но делает между передачами лишь короткие паузы длиной LLQI (Last Listener Query Interval), по умолчанию 1 секунда [§9.8 RFC 3810]. При этом слушатели задерживают свои отчеты на короткое случайное время от 0 до LLQI. В такой схеме самое позднее, когда может прийти отчет, это спустя время $$LLQI\times LLQC$$ от начала проверки, что по умолчанию составляет 3 секунды. Для этого интервала принято обозначение LLQT (Last Listener Query Time) [§9.10 RFC 3810]. И нет смысла устанавливать таймер источника во время проверки на больший интервал.

    Нам трудно избавиться от навязчивой мысли, что аббревиатуру LLQT следует произносить так: "Lil’ cutie".

    Интервал LLQI входит в LLQT ровно LLQC раз, а не LLQC + 1, потому что первый запрос в серии уходит немедленно. Затем следует LLQC - 1 интервалов повтора и, наконец, еще один интервал отведен на случайную задержку отчетов слушателями.

    Выходит, что в зависимости от ситуации оптимальный интервал таймера может быть длинным (

  • По умолчанию маршрутизатор устанавливает таймер источника на длинный интервал (MALI).
  • Когда маршрутизатор передает первое сообщение в серии запросов Q(Г,S), ограниченных данной группой Г и списком источников S, он понижает значение таймера на каждом источнике группы Г из списка S до LLQT, если оно выше LLQT.
  • Чуть позднее нам придется видоизменить эту процедуру, чтобы обеспечить более надежную синхронизацию между генератором запросов и пассивными наблюдателями. В некоторых случаях маршрутизатору придется устанавливать значение таймера на интервал LLQT при передаче не только первого, но и нескольких последующих запросов в серии.

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

    Прекрасно, теперь маршрутизатор может гибко управлять таймерами источников, пока фильтр группы работает во включающем режиме. А как быть с исключающим режимом? Будут ли его правила игры совершенно иными? Для начала напомним себе, что в этом режиме на месте единственного списка включений, содержавшего в себе все записи об источниках, возникает целых два списка, требований и исключений, а запись об источнике находится в одном из них. Функция списка требований такова, что и управлять им надо практически так же, как списком включений. Единственное существенное отличие заключается в том, что по тайм-ауту его записи не удаляются, а перемещаются в список исключений, потому что это кандидаты на блокировку. Оказавшись в списке исключений, запись об источнике вообще становится "бессмертной", а ее таймер не работает.

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

    Пока конфигурация слушателей постоянна, это должен быть длинный интервал (MALI), так как подготовка к переходу EXCLUDE$$\to$$INCLUDE включает в себя накопление записей в списке требований, а для этого каждый слушатель во включающем режиме должен получить возможность высказаться отчетом IS_IN в ответ на последний из периодических запросов маршрутизатора.

    Но возможна и ситуация, когда маршрутизатор получает повод немедленно усомниться в целесообразности исключающего режима. Таким поводом служит отчет TO_IN, косвенно говорящий об уменьшении числа слушателей группы в исключающем режиме. Получив его, маршрутизатор должен каким-то образом проверить, не упало ли оно до нуля. С этой целью маршрутизатор шлет серию из запросов Q(Г), ограниченных только группой, но не списком источников, пытаясь вызвать отклик слушателей группы в исключающем режиме за время LLQT. Наряду с ними ответят и все остальные слушатели группы, так что время подготовки к вероятному переходу EXCLUDE$$\to$$INCLUDE, если он произойдет ввиду отсутствия ожидаемого отклика, можно смело сократить до LLQT.

    По итогам наших рассуждений составим процедуру управления интервалом таймера фильтра — она оказывается практически такой же, как у таймера источника:

  • По умолчанию маршрутизатор устанавливает таймер фильтра на длинный интервал (MALI).
  • Когда маршрутизатор передает первое сообщение в серии запросов Q(Г), ограниченных только данной группой Г, он понижает значение таймера фильтра группы Г до LLQT, если оно выше LLQT.
  • Позднее нам придется видоизменить и эту процедуру, чтобы обеспечить надежную синхронизацию между генератором запросов и пассивными наблюдателями. В некоторых случаях маршрутизатору придется устанавливать значение таймера фильтра на интервал LLQT при передаче не только первого, но и нескольких последующих запросов в серии.

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

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

    Таким образом, у каждой разновидности запроса MLDv2 своя отдельная роль. Все они собраны в Табл. 8.4.

    Разновидности запросов MLDv2
    Разновидность Обозначение Когда шлется Роль
    Общий запрос Q(::) При старте маршрутизатора, затем периодически. Выяснить текущее состояние дел на канале.
    Запрос, ограниченный группой Г Q(Г) Когда уменьшается число слушателей группы в исключающем режиме. Выяснить, остались ли у данной группы слушатели в исключающем режиме.
    Запрос, ограниченный группой и списком источников Q(Г, S) Когда уменьшается число слушателей группы, заинтересованных в данных источниках S. Выяснить, востребованы ли все еще в данной группе данные источники.

    И вот, наконец, мы готовы составить свод правил для маршрутизатора MLDv2 в Табл. 8.5.

    Правила обработки отчетов маршрутизатором MLDv2
    Текущее состояние Отчет Новое состояние Запросы Удалить Таймеры
    INCLUDE($$A$$ ) ALLOW( $$B$$) INCLUDE$$(A\cup B)$$ - - ($$B$$ )=MALI
    INCLUDE($$A$$ ) IS_IN($$B$$ ) INCLUDE$$(A\cup B)$$ - - ( $$B$$)=MALI
    INCLUDE($$A$$ ) TO_IN( $$B$$) INCLUDE$$(A\cup B)$$ $$Q(Г,A\backslash B)$$ - ( )=MALI
    Комментарий: Текущий фильтр пропускает только источники из множества . Поэтому все три отчета, по сути, требуют расширения этого множества до $$A\cup B$$. На записях, о которых получена свежая информация, перезапускается таймер. Отчет TO_IN(B) также вызывает запрос насчет прочих допущенных источников, $$Q(Г,A\backslash B)$$, потому что это может быть сигнал о сокращении или изменении, а не расширении множества принимаемых источников. Отчет IS_IN(B) такого запроса не вызывает, потому что он сам пришел в ответ на какой-то запрос, во избежание зацикливания протокола.
    INCLUDE($$A$$) BLOCK($$B$$) INCLUDE($$A$$) $$Q(Г,A\cap B)$$ - -
    Комментарий: Поскольку текущий фильтр пропускает только источники из множества $$A$$ , подмножество уже заблокировано, и остается рассмотреть только $$A\cap B$$. Маршрутизатор не может немедленно заблокировать его, так как его потенциально принимают другие слушатели, поэтому сейчас он ограничивается запросом $$Q(Г,A\cap B)$$.
    INCLUDE($$A$$) IS_EX($$B$$) EXCLUDE($$A\cap B, B\backslash A)$$ - $$ A\backslash B $$ $$( B\backslash A)=0$$, (фильтр)=MALI
    INCLUDE($$A$$) TO_EX($$B$$) EXCLUDE($$A\cap B, B\backslash A$$) $$Q(Г,A\cap B)$$ $$ A\backslash B $$ $$( B\backslash A )=0 $$,(фильтр)=MALI
    Комментарий: Прежде всего, маршрутизатор должен перевести фильтр в исключающий режим, так как обнаружился слушатель в этом режиме. Все источники, кроме множества A , уже были заблокированы, поэтому сейчас можно смело блокировать такую часть B : $$B\cap (\backslash A)=B(\backslash A$$. Таймеры этих записей останавливаются, так как у списка исключений один таймер на весь фильтр; он как раз запускается. Остаток B , то есть $$A\cap B$$, немедленно блокировать нельзя, но его можно подвергнуть проверке, сохранив в списке требований. Тем не менее, запрос об источниках $$A\cap B$$ можно послать, только если отчет — об изменении фильтра (TO_EX), а не о его текущем состоянии (IS_EX), потому что последний сам пришел в ответ на какой-то запрос. Источники $$A \backslash B $$ в новом состоянии допущены по умолчанию, и записи о них можно вообще удалить.
    EXCLUDE($$X,Y$$ ) ALLOW($$A$$) EXCLUDE($$X\cup A, Y\backslash A$$) - - ($$A$$)=MALI
    EXCLUDE($$X,Y$$) IS_IN($$A$$) EXCLUDE($$X\cup A, Y\backslash A$$) - - ($$A$$)=MALI
    EXCLUDE($$X,Y$$) TO_IN($$A$$) EXCLUDE($$X\cup A, Y\backslash A$$) Q(Г,$$X\backslash A$$ ), Q(Г) - ($$A$$)=MALI

    Комментарий: Здесь мы знакомимся со второй ролью списка требований X, когда он накапливает включающие записи на случай перехода фильтра во включающий режим. Поэтому недостающие записи добавляются в список требований путем объединения множеств X и A. На всех записях, о которых получена свежая информация, перезапускается таймер. В то же время элементы A удаляются из списка исключений, который сокращается до $$ Y\backslash A$$. Ведь отчет требует прекратить блокировку A, и это должно произойти немедленно, так как погрешность фильтра допустима только в сторону разрешения источников.

    Отчет IS_IN запросов не вызывает, так как сам вызван запросом. Напротив, отчет TO_IN вызывает запрос насчет тех элементов X, о которых нет свежей информации: X за исключением A. По большому счету, он нужен, чтобы список X не рос за счет устаревших записей. Кроме того, отчет TO_IN потенциально означает, что число слушателей в исключающем режиме уменьшилось, потому что один из них перешел во включающий режим. Чтобы проверить, остались ли они вообще, маршрутизатор шлет еще один запрос без уточнения источников, то есть ограниченный только группой: Q(Г). Хотя в этом случае запрос $$Q(Г, X\backslash A)$$ может показаться избыточным, он нужен для координации таймеров между маршрутизаторами канала — о ней мы скоро поговорим.

    EXCLUDE($$X,Y$$) IS_EX($$A$$) EXCLUDE($$ A\backslash Y, Y\cap A$$) - ($$ X\backslash A, Y\backslash A $$) ($$ A\backslash X\backslash Y $$)=MALI,(фильтр)=MALI
    EXCLUDE($$X,Y$$) TO_EX($$A$$) EXCLUDE($$ A\backslash Y, Y\cap A$$) Q(Г,$$ A\backslashY $$ ) $$ X\backslash A, Y\backslash A $$) ($$ A\backslash X\backslash Y $$)=MALI, (фильтр)=MALI

    Комментарий: Безопасно блокировать можно только пересечение $$ Y\cap A $$, так как только насчет этого подмножества есть консенсус слушателей. Остаток Y , то есть $$ Y\backslash A $$, нужно немедленно разблокировать, потому что он интересен данному слушателю. Остальную часть A , то есть $$ A\backslash Y $$, надо сначала проверить запросом, если мы имеем дело с отчетом об изменении состояния (TO_EX).

    При этом роль списка требований меняется: он снова хранит записи о проверяемых источниках, кандидатах на блокировку, а все прочие записи из него удаляются. Подмножество $$ X\backslash A $$ уходит из списка требований, потому что в новых условиях у него больше нет шансов на блокировку: слушатель хочет его принимать. В то же время, подмножество $$ X\cap A $$ остается в списке требований, потому что это по-прежнему кандидат на блокировку.

    На вновь добавленных записях, которых раньше не было в списке требований (это $$ A\backslash X\backslash Y $$), запускаются таймеры. В случае TO_EX они устанавливаются на интервал, равный текущему значению таймера фильтра. Почему так? Пока фильтр работает в исключающем режиме, его таймер отсчитывает время до желанного момента, когда маршрутизатор сможет заблокировать почти все источники. Поэтому таймер фильтра неявно "тикает" на всех источниках, которые сейчас не занесены в или и потому разрешены по умолчанию. Ради сохранения информации об актуальности таких записей, мы должны скопировать значение таймера фильтра в таймер такого источника, когда мы создаем о нем явную запись в списке требований . Именно это здесь и происходит: сохранение накопленной информации.

    Случай IS_EX особенный. Если маршрутизатор получил сообщение IS_EX, то есть заявление об уже существующем состоянии слушателя, и подмножество $$ A\backslash X\backslash Y $$ не пусто, то это значит, что маршрутизатор потерял синхронизацию со слушателем. Хотя такое может произойти, например, когда маршрутизатор едва только включился в работу, или же после сбоя канала, наиболее вероятная причина — что маршрутизатор сам удалил эти записи из списка требований, чтобы проверить другие источники. Скажем, именно это произойдет, если на канале несколько слушателей в исключающем режиме, фильтры которых не совпадают: список требований станет "осциллировать". (Убедитесь в этом, изучив сценарий, когда один слушатель блокирует множество {1,2}, а другой — {2,3}.) Так или иначе, маршрутизатор не может доверять своим текущим сведениям и устанавливает таймеры новых записей на длинный интервал MALI. Кроме того, маршрутизатор не шлет запрос, ограниченный группой и списком источников, так как отчет IS_EX уже вызван каким-то запросом.

    У нас есть гипотеза, что установка ($$ A\backslash X\backslash Y $$)=(фильтр) и в этом случае не вызвала бы никаких проблем. Исследуйте ее, если будет время и желание.

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

    EXCLUDE($$X,Y$$) BLOCK($$A$$) EXCLUDE$$(X\cup ( A\backslash Y),Y)$$ ( Q(Г, $$A\backslash Y $$) - ($$ A\backslash X\backslash Y $$)=(фильтр)
    Комментарий: Это правило похоже на предыдущее, TO_EX. Часть множества A, возможно, уже заблокирована фильтром Y, поэтому изменения касаются только подмножества $$ A\backslash Y $$. Как обычно, вместо его непосредственной блокировки следует запрос о нем. Тем не менее, отчет BLOCK может исходить от слушателя в любом режиме, и поэтому маршрутизатор только добавляет недостающие элементы в список требований X путем объединения множеств, а не сбрасывает его до $$ A\backslash Y $$. По той же самой причине таймер фильтра продолжает отсчет времени, а не перезапускается; ведь отчет BLOCK не доказывает, что есть слушатели в исключающем режиме. Если его интервал вскоре истечет, фильтр перейдет во включающий режим, а в список включений попадут все желательные источники, потому что список требований не сбрасывался до проверяемого подмножества, а объединялся с ним. Так маршрутизатор избежит нежелательной блокировки источников при переключении фильтра. Об установке таймеров на источниках см. предыдущее обсуждение TO_EX. В отличие от правила TO_EX, здесь список исключений Y не требует немедленных изменений. Дело в том, что объявление вида TO_EX(A) говорит, что данному слушателю интересны все источники кроме перечисленных во множестве A, а значит, их надо немедленно разблокировать. В то же время, объявление BLOCK(A) не несет в себе такого строгого требования.

    В отличие от правила TO_EX, здесь список исключений не требует немедленных изменений. Дело в том, что объявление вида TO_EX(A) говорит, что данному слушателю интересны все источники кроме перечисленных во множестве A, а значит, их надо немедленно разблокировать. В то же время, объявление BLOCK(A) не несет в себе такого строгого требования.

    Заданием для читателя будет изобразить и проанализировать диаграммы Эйлера, отвечающие этим правилам.

    Также убедитесь, что отчеты идемпотентны, то есть несколько копий одного отчета подряд, приведут маршрутизатор в то же самое состояние, что и ровно один экземпляр отчета. Это свойство позволяет слушателям повторять свои отчеты не по запросу, чтобы они наверняка дошли.

    А обладают ли отчеты свойствами коммутативности и транзитивности? (Повод подумать.)

    Все отчеты кроме IS_EX и TO_EX обладают свойством аддитивности: если данный список источников разбить, скажем, на два подсписка и послать их в разных отчетах, то окончательное состояние маршрутизатора окажется тем же самым. Зачем это может пригодиться, догадаться несложно: если отчет о данной группе содержит так много источников, что сообщение превышает MTU канала, его можно разбить на несколько сообщений поменьше. Однако для IS_EX и TO_EX этот трюк не работает. Как тогда быть? Приемлемым решением будет передать только часть списка одним сообщением [§5.2.15 RFC 3810]. Ведь маршрутизатор MLD вправе ошибаться в разрешительную сторону, если это необходимо, а опустить часть источников в отчете IS_EX или TO_EX, значит, допустить их к группе.

    Подсчитайте, сколько источников может уместиться в одно сообщение MLDv2, когда MTU канала равно минимально возможному значению 1280 байт. (Подсказка: учесть опцию "сигнал маршрутизатору. Ответ: {1280[MTU]-40[IP]-8[HopByHop-RtrAlert]-28[MLDv2]} div 16 = 75.)

    Оказывается, что сложность правил MLDv2 (а также IGMPv3) обусловлена, главным образом, поддержкой слушателей в режиме EXCLUDE(A), где множество A не пусто. Рассмотрите упрощенный протокол LW MLDv2 (облегченный MLDv2), где слушатель может принимать или избранные источники (INCLUDE(A)), или все источники подряд (EXCLUDE($$ \varnothing$$)), но не может блокировать избранные источники. В этом случае правила маршрутизатора становятся намного проще, потому что ему больше не нужно вести список исключений. Самое удивительное, что маршрутизатор LW MLDv2 может быть совместим со слушателями MLDv2, потому что в MLD допустима погрешность в сторону разрешения источников. Маршрутизатору LW MLDv2 достаточно рассматривать любые отчеты IS_EX(A) или TO_EX(A) как просьбу разрешить все источники, то есть IS_EX($$ \varnothing$$) или TO_EX($$ \varnothing$$), соответственно. Если вы не справитесь с этим заданием самостоятельно, обратитесь к [RFC 5790].

    Такое выражение протокола MLDv2 на языке теории множеств имеет одну любопытную особенность. Когда составленная нами таблица предписывает послать запрос, ограниченный группой и списком источников, может так оказаться, что список источников пуст. Например, в последнем правиле таблицы, EXCLUDE/BLOCK, такая ситуация возникнет, если A — это подмножество Y: $$A\subseteq Y\Rightarrow A\backslash Y= \varnothing$$. На первый взгляд, выходит, что надо послать запрос Q(Г, $$ \varnothing$$), где $$ \varnothing$$ — пустое множество. Но, если мы составим такой запрос, то он окажется неотличим от запроса, ограниченного только группой, Q(Г) — см. формат запроса MLDv2. Как разрешить эту двусмысленность? На самом деле, очень просто: запрос Q(Г, $$ \varnothing$$) риторический и никакого ответа не предполагает, а значит, и слать его совершенно ненужно. Ведь запрос всегда ставится в режиме INCLUDE, и на пустой список источников ответа никогда не было бы в принципе. Иными словами, единственный режим слушателя, который мог бы вызвать ответ на Q(Г, $$ \varnothing$$), — это INCLUDE($$ \varnothing$$), но этот режим эквивалентен прекращению приема группы.

    Выражаясь на модном сегодня новоязе а-ля "1984", это не-режим не-слушателя.

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

    В связи с этой процедурой возникает таймер, с помощью которого наблюдатель может обнаружить, что текущий генератор запросов ушел со сцены. Этот таймер перезапускается на интервал OQPT (Other Querier Present Timeout, тайм-аут присутствия другого генератора запросов) всякий раз, когда приходит запрос MLD. Если же происходит тайм-аут, значит, запросов давно никто не слал, и пора начать новые выборы. Стандарт предлагает величину OQPT, равную $$QI\times RV+\frac{1}{2}QRI$$ [§9.5 RFC 3810]. Однако на наш взгляд, добавка $$\frac{1}{2}QRI$$ способна привести к тому, что новый генератор запросов возникнет слишком поздно и непрерывность маршрутизации группового трафика будет нарушена. Убедиться в этом просто. Рассмотрите канал с одним слушателем и двумя маршрутизаторами. Один из маршрутизаторов, очевидно, генератор, а второй наблюдатель. Когда генератор исчезает, наблюдатель ждет время OQPT, прежде чем послать серию запросов. Затем слушатель вправе задержать свой отчет на время вплоть до QRI. В результате отчет может запоздать, придя через время $$ OQPT+QRI-MALI=\frac{1}{2}QRI$$ после того, как оставшийся маршрутизатор прекратил продвижение группы в канал по тайм-ауту MALI. Более подходящим значением для OQPT будет просто $$QI\times RV$$.

    Когда маршрутизатор только включается в работу или участвует в выборах, ему надо позаботиться, чтобы его запрос наверняка услышали другие маршрутизаторы, а также слушатели. Ради устойчивости к потере пакетов он передает такой запрос несколько раз (по умолчанию RV [§9.7 RFC 3810]), разделяя повторы паузами (по умолчанию $$\frac{1}{4}QRI$$ [§9.6 RFC 3810]).

    После выборов все маршрутизаторы кроме генератора запросов замолкают и ведут только пассивное наблюдение за чужими сообщениями MLD. Чтобы такая система работала устойчиво, состояние наблюдателей должно быть согласовано с генератором в том, что касается управляющих записей и таймеров. Для этого, в первую очередь, наблюдатели должны получить от генератора его текущие значения параметров QI и RV, так как от них зависит длина интервала MALI. Эти значения обозначают, соответственно, QQI (Querier’s QI) и QRV (Querier’s RV), чтобы подчеркнуть, что они исходят от генератора запросов.

    Как следствие, формат запроса MLDv2 должен предусмотреть поля для этой информации. Значение QRV можно поместить в пакет как есть, в беззнаковом коде. Соответствующее поле тоже обозначают QRV (Querier’s Robustness Variable, RV генератора запросов) [§5.1.8 RFC 3810]. В то же время, значение QQI, если представить его как число миллисекунд в 16-битном беззнаковом коде, может расти только до 65,535 секунд, а кроме того, миллисекундная точность на всем диапазоне значений здесь излишня. Поэтому для передачи QQI мы чуть позже разработаем новый код. Он же пригодится и для передачи значений MRD (максимальной задержки отчета). Поле для хранения QQI в этом коде обозначают QQIC (Querier’s Query Interval Code, код интервала запросов генератора) [§5.1.9 RFC 3810].

    Далее, когда дело доходит до быстрой проверки группы, в игру вступают интервал LLQI, счетчик повторов LLQC и тайм-аут LLQT. Значения этих параметры тоже должны быть согласованными, чтобы наблюдатель не удалил запись об источнике или не переключил режим фильтра группы раньше времени. Впрочем, эти параметры явным образом передавать не надо. Вот наше тому обоснование:

  • По умолчанию значение LLQC совпадает с RV, а рекомендуемое значение RV уже объявляется генератором запросов в поле запроса QRV.
  • При быстрой проверке максимальная задержка отчета, MRD, равна LLQI, а значение параметра MRD уже содержится в запросе. Поэтому наблюдателям достаточно выполнить обратную операцию и принять рабочее значение LLQI равным MRD из полученного запроса.
  • Значение LLQT однозначным образом зависит от LLQI и $$LLQC$$: $$LLQI \times LLQC$$.
  • Чтобы не быть голословными, сошлемся на существующие реализации. Например, именно так поступает модуль MLDv2 в XORP http://www.xorp.org/ : он принимает LLQC равным QRV, а LLQI равным MRD, если принят запрос с ненулевым адресом группы.

    Но каким образом наблюдатели узнают, что началась быстрая проверка? Эту информацию можно извлечь из того, какого вида запрос:

  • Если запрос общий, с нулевым адресом группы и пустым списком источников, то это явно не быстрая проверка, а начальный или периодический запрос. Из такого запроса надо извлечь только значения RV и QI.
  • Если запрос ограничен только ненулевой группой, а список источников в нем пустой, то это быстрая проверка, остались ли на канале слушатели группы в исключающем режиме. В этом случае надо понизить интервал таймера на фильтре указанной группы до LLQT. Согласно принципу Постела, получатель запроса должен сначала убедиться, что фильтр указанной группы работает в исключающем режиме.
  • Если же запрос ограничен ненулевой группой и непустым списком источников, то он служит для быстрой проверки указанных источников данной группы. Для наблюдателя это повод понизить интервалы таймеров на всех перечисленных источниках данной группы до LLQT.
  • Конечно, наблюдателю не помешало бы убедиться, что запрос исходит именно от генератора запросов. Пунктуальная реализация может помнить адрес текущего генератора и обновлять его, если пришел запрос с еще меньшим адресом источника IPv6. Однако в этом случае придется также учесть сценарий с исчезновением генератора. На практике достаточно сравнить адрес источника запроса с собственным адресом наблюдателя и проигнорировать запрос, если наш локальный адрес меньше. Остальную работу по стабилизации системы здесь выполнит механизм выборов генератора. Ведь он гарантирует , что в конце концов останется ровно один активный генератор, который и станет диктовать остальным значения параметров.

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

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

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

    Какие сложности это вызывает? В нашей рабочей схеме каждый экземпляр запроса управляет таймерами наблюдателей, понижая их интервал до небольшой величины LLQT. Поэтому возможен, к примеру, такой сценарий с участием генератора, наблюдателя и слушателя, составленный для LLQC = 2:

  • Генератор шлет первый экземпляр запроса Q(Г,S).
  • Наблюдатель понижает таймеры на источниках S до LLQT.
  • Слушатель после короткой паузы отвечает отчетом, подтверждая свой интерес к источникам S.
  • Генератор и наблюдатель получают отчет и перезапускают таймеры S с длинным интервалом MALI.
  • Генератор повторяет запрос Q(Г,S).
  • Наблюдатель понижает таймеры на источниках S до LLQT.
  • Слушатель не отвечает или отчет не доходит до наблюдателя.
  • Наблюдатель получает тайм-аут на источниках S и блокирует их.
  • Синхронизация генератор-наблюдатель нарушена!
  • Аналогичный сценарий можно составить и для запроса Q(Г), ограниченного только группой. В нем у наблюдателя сработает таймер фильтра.

    К сожалению, генератор запросов действует в условиях недостатка информации и поэтому не может дать стопроцентную гарантию того, что наблюдатель получит все необходимые сведения даже после сбоя сети. Видимо, лучшее, что мог сделать генератор на шаге 5 нашего сценария, — это сообщить наблюдателю: больше не понижай таймеры, так как источники S все еще востребованы в данной группе. Эту информацию можно закодировать одним битом в формате запроса. По смыслу этот бит — просто флаг, надо ли наблюдателям понижать соответствующие таймеры. Он возникнет на месте поля "резерв", поэтому его значение 1 отвечает новому поведению: не понижать таймеры. Отсюда его название: "подавить обработку маршрутизаторами" (Suppress Router-Side Processing), сокращенно "флаг S" [§5.1.7 RFC 3810].

    Как генератор запросов определит, что пора установить этот флаг? Если мы предположим, что слушатели подтвердили свой интерес ко всем источникам множества S в запросе, то будет достаточно отметить этот факт установкой флага S в том образе запроса, который генератор хранит в своей оперативной памяти. Тогда последующие копии запроса уйдут с S = 1. Однако в действительности может оказаться, что подтвержден интерес только к подмножеству источников $$S_1\subset S$$. Как генератору запросов разделить подмножества $$S_1$$ и $$S_2=S\backslash S_1$$, когда придет время повторить запрос? Здесь ему помогут значения таймеров. Ведь у подтвержденного подмножества таймеры перезапущены и снова отсчитывают длинный интервал MALI, тогда как у неподтвержденного подмножества значения таймеров по-прежнему не превышают LLQT. Так как флаг S один на все сообщение, генератору придется разбить запрос на два. В первом из них будет перечислено подмножество источников $$S_1$$ и установлен флаг S (S = 1); во втором же окажется подмножество источников $$S_2$$, а флаг S будет сброшен (S = 0).

    Флаг S имеет смысл и для запросов, ограниченных только группой. В этом случае речь идет о подтверждении исключающего режима. Так как маршрутизатор не может находиться в нем наполовину, делить будущие копии запроса ему точно не придется. Тем не менее, критерий, установить ли флаг S, может быть тем же, с точностью до замены таймера источника на таймер фильтра: если таймер фильтра по-прежнему не превышает LLQT, то S = 0, а если он вдруг стал больше LLQT, то значит, пора установить S = 1.

    Теперь наблюдатели понижают таймеры не по любому запросу Q(Г) или Q(Г,S), а только если в нем S = 0. Для нас это повод уточнить условия, когда то же самое делает генератор запросов. Если бы наблюдателей не было вообще, генератор мог бы сделать это единожды, в самом начале серии запросов. Ведь вся наша арифметика тайм-аутов MALI и LLQT была основана именно на этой модели. С другой стороны, если бы наблюдатели существовали, но пакеты всегда достигали бы цели, генератор тоже мог бы понизить таймеры в начале серии, а флаг S установить сразу после первого запроса, чтобы наблюдатели поступили с таймерами так же. То есть первый запрос серии вызвал бы установку таймеров на всех маршрутизаторах канала, а последующие дубликаты запроса на них бы не влияли. Но в действительности первый запрос серии может потеряться, как любой другой. Именно поэтому генератор запросов устанавливает флаг S не раньше, чем он получит подтверждение, что энный запрос хоть до кого-то дошел (в данном случае до слушателя). Поэтому пусть ради синхронизации с наблюдателями генератор тоже продолжает понижать соответствующие таймеры до LLQT всякий раз, пока он передает запрос с S = 0.

    Конечно, этот трюк нарушает четкость нашего плана, как согласовать тайм-аут записи с ритмом повторяемых запросов, но он помогает синхронизации маршрутизаторов канала. С одной стороны, теперь время жизни записи может продлиться дольше, чем это необходимо. Так, если заинтересованных слушателей не осталось и на запрос ответить некому, запись проживет интервал LLQT после последнего запроса, хотя по нашему первоначальному плану она прекратила бы свое существование через минимально необходимое время LLQI. С другой стороны, теперь выше вероятность того, что тайм-аут записи произойдет практически одновременно на всех маршрутизаторах.

    Окончательные правила синхронизации между генератором запросов и пассивными слушателями оказываются такими:

  • Подготовка к передаче запроса — только для генератора [§7.6.3 RFC 3810]:

    a. Запрос, ограниченный только группой Г: Если таймер фильтра Г меньше или равен LLQT, то флаг S надо сбросить в 0, а иначе установить в 1.

    b. Запрос, ограниченный группой Г и списком источников S: Если все таймеры S меньше или равны LLQT, то флаг S надо сбросить в 0, а если больше — установить в 1. В смешанном случае запрос надо разбить на два, один с S = 0, а другой с S = 1.

  • Действия по приему или передаче запроса — как для генератора, так и наблюдателей [§7.6.1 RFC 3810]:

    a. Если в запросе флаг S сброшен в 0:

    i. Запрос, ограниченный только группой Г: Понизить таймер фильтра Г до LLQT.

    ii. Запрос, ограниченный группой Г и списком источников S: Понизить таймеры источников из списка S до LLQT.

    b. Если же в запросе флаг S установлен в 1, то текущие таймеры не трогать.

  • Эта процедура действительно способна помочь синхронизации между генератором запросов и наблюдателями после кратковременного сбоя в сети. Например, если в нашем вышеизложенном сценарии самый первый отчет слушателя дойдет до наблюдателя, но не дойдет до генератора, наблюдатель повысит таймеры до MALI, тогда как у генератора они продолжат отсчитывать короткий интервал LLQT. Однако затем генератор повторит свой запрос с S = 0, и наблюдатель снова понизит таймеры, так что синхронизация будет восстановлена.

    На этом мы почти завершили усовершенствование MLD и практически готовы объявить вторую версию протокола готовой к применению. Последнее, о чем нам следует подумать, — это совместимость с первой версией.

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

    Совместимость сообщений — это, по сути, однозначность их интерпретации. Скажем, если в запросе MLDv2 мы изменим код поля "максимальная задержка отклика" , В MLDv1 это поле обозначалось как MRD. то нам надо позаботиться, чтобы слушатель MLDv1 не применил к этому полю ошибочную интерпретацию. Продемонстрируем это на практике. Мы как раз собирались разработать новый код для значений QI и MRD в запросе, так что давайте сделаем его обратно совместимым с целым беззнаковым кодом.

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

    Наша цель — представить широкий диапазон значений с разумной точностью, а этому условию отвечает код с плавающей запятой, похожий на экспоненциальную (научную) нотацию чисел. В этом коде число приближенно записывают как мантиссу в оптимальном диапазоне, помноженную на степень основания системы счисления, в данном случае 2:$$x=M\cdot 2^E$$. Здесь M — мантисса фиксированной точности, а E — целочисленный порядок числа.

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

    Хотя обычно принимают, что в нормализованной записи запятая находится после самого старшего разряда мантиссы, нам сейчас удобнее поместить ее после самого младшего разряда, чтобы мантисса оставалась целой. Очевидно, что эти два представления однозначно преобразуются друг в друга простым сдвигом, то есть изменением порядка E на число явно хранимых разрядов мантиссы, если старший разряд подразумевается. Например, двоичная мантисса 1,0001 преобразуется в 10001 вычитанием 4 из порядка, чтобы значение закодированного числа оставалось неизменным.

    Применим такой код к 16-битному полю, где будет храниться значение MRD. Ввиду нового кода это поле и обозначают по-другому: MRC (Maximum Response Code, код максимальной задержки отклика) [§5.1.3 RFC 3810]. Пусть порядок занимает 3 бита. Еще один бит "съел" флаг кода. Тогда на мантиссу остается 12 бит. Значения до 32767 мы уже можем представить в старом коде, поэтому нам интересен диапазон от $$32768=2^{15}$$ и выше. Этому отвечают значения порядка от 3: $$\log_2 \frac{2^{15}}{2^{12}}=3$$. Поэтому нам следует сместить порядок на 3 для расширения диапазона. Двоичные значения поля мантиссы и поля порядка будут представлены в обычном целом беззнаковом коде. Тогда численное значение MRD в новом коде будет равно $$(0x1000+M^\prime)\cdot 2^{E^\prime+3}$$.

    Рассчитайте, каковы минимальное и максимальное значения поля MRC в новом коде. (32 768 и 8 387 584.)

    Тем же самым способом мы решим задачу о кодировании поля QQIC для хранения значений QQI [§5.1.9 RFC 3810]. Это поле длиной 8 бит, причем 3 из них отведены под порядок и 4 под мантиссу. Следовательно, численное значение поля, когда его старший бит установлен, следует вычислять так: $$QQI=(0x10+M^\prime)\cdot 2^{E^\prime+3}$$.

    Рассчитайте, каковы минимальное и максимальное значения поля QQIC в новом коде. (128 и 31 744.)

    Расширенные форматы полей MRC и QQIC в отчете MLDv2 показаны на рис. 8.8.

    (рис 8.8) Код с плавающей запятой для полей MRC и QQIC

    Решив вопрос о совместимости на уровне отдельных полей, перейдем к совместимости формата сообщений в целом. По своему формату отчет MLDv2 в корне отличается от отчета MLDv1, так как он содержит переменное число записей о разных группах, а каждая запись имеет определенный тип и может содержать список источников. Лучшее, что мы можем сделать ради совместимости как однозначности интерпретации, — это полностью разделить отчеты MLDv1 и MLDv2. Для этого достаточно назначить отчету MLDv2 новый тип ICMPv6 (143).

    Что же касается запросов, то их формат изменился не так радикально. Более того, он вполне отвечает модели расширения протокола MLD, когда новые данные помещаются в конец сообщения, после его совместимой части. Благодаря этому запрос MLDv2 может сохранить за собой тот же самый тип ICMPv6 (130). Если реализация поддерживает обе версии MLD, то ей просто определить версию запроса на входе: запрос MLDv1 всегда длиной 24 байта, тогда как длина запроса MLDv2 — от 28 байт и выше. Диапазон длин от 25 до 27 байт запретный и не отвечает никакой версии MLD.

    Обратите внимание, что длина сообщения MLDv2 — как запроса, так и отчета — закодирована в самом сообщении. Поэтому отличить будущие версии от MLDv2 можно будет, только декодировав совместимую с MLDv2 часть.

    Наконец, мы добрались до совместимого поведения сторон протокола. Здесь нас, в первую очередь, интересуют два случая: поведение слушателей MLDv1 в ответ на запросы MLDv2 и поведение маршрутизаторов MLDv2 в ответ на отчеты MLDv1. Это видно из таблиц совместимости Табл. 8.6 и Табл. 8.7.

    Совместимость слушателей MLD
    Версия слушателя Запрос MLDv1 Запрос MLDv2
    MLDv1 нет проблем как быть?
    MLDv2 отчет MLDv1 нет проблем
    Совместимость маршрутизаторов MLD
    Версия маршрутизатора Отчет или итог MLDv1 Отчет MLDv2
    MLDv1 нет проблем проигнорирует
    MLDv2 как быть? нет проблем

    Слушатели MLDv2 не должны отвечать отчетами MLDv2 на запросы MLDv1 хотя бы потому, что маршрутизатор MLDv1 проигнорирует их ввиду неизвестного типа ICMPv6.

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

    Это естественным порядком приводит нас ко второму случаю. Маршрутизатор MLDv2 как более современный легко декодирует сообщение MLDv1, поэтому вопрос состоит в том, насколько усложнит реализацию режим совместимости. В частности, понадобится ли маршрутизатору MLDv2 отдельный алгоритм реакции на сообщения MLDv1? К счастью, нет, потому что по содержащейся в них информации отчет и итог MLDv1 — это лишь подмножество многофункциональных отчетов MLDv2. На самом деле, мы об этом уже говорили, так что сейчас нам осталось только составить таблицу соответствия — Табл. 8.8.

    Апгрейд" сообщений MLDv1 до MLDv2
    Сообщение MLDv1 Эквивалент MLDv2
    Отчет IS_EX($$ \varnothing $$)
    Итог TO_IN($$ \varnothing $$)

    Благодаря такому соответствию, маршрутизатор MLDv2 может обрабатывать отчеты и итоги MLDv1 по уже готовым правилам протокола MLDv2.

    Конечно, маршрутизатору MLDv2 придется учесть, что адрес назначения отчета или итога MLDv1 равен группе, о которой идет речь, а не FF02::16. Это не вызовет проблем, так как маршрутизатор ведет теневой прием всех групп и руководствуется, в первую очередь, опцией "сигнал маршрутизатору" в сообщениях MLD, а не их адресами назначения.

    Помимо этих двух очевидных случаев взаимодействия сторон MLD, нам надо обратить внимание на "перекрестное опыление" слушателей и маршрутизаторов (Табл. 8.9). Ведь в принципе слушатели могут получать отчеты, а маршрутизаторы — запросы. К счастью, с первой частью проблем нет, так как отчеты MLDv2 направляются по специальному групповому адресу, и слушатели их не получат, независимо от их версии. Что касается реакции слушателя MLDv2 на отчет MLDv1, связанная с этим оптимизация (подавление отчетов) упразднена, и отчет можно безопасно игнорировать.

    Тем не менее, слушателю MLDv2 не запрещено подавлять свои отчеты при получении чужих отчетов MLDv1 [§8.2.2 RFC 3810].

    Перекрестное опыление" слушателей MLD
    Версия слушателя Отчет или итог MLDv1 Отчет MLDv2
    MLDv1 нет проблем не получит
    MLDv2 может игнорировать не получит

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

    Поэтому, ради устойчивой работы сети, мы объявим нежелательной смешанную конфигурацию с использованием маршрутизаторов MLDv1 и MLDv2 на одном канале. Точнее, пусть маршрутизатор MLDv2 использует только запросы MLDv1, пока на том же канале есть маршрутизаторы MLDv1 [§8.3.1 RFC 3810]. Это приемлемый компромисс, так как маршрутизаторов меньше, чем хостов, а их взаимозаменяемость выше. Если обновление хостов — процесс долгий , трудоемкий и требующий поэтапного подхода, переключение подсети В старом смысле этого термина: транзитная инфраструктура сети. на MLDv2 можно провести за один шаг, например, путем установки на канал одного или нескольких новых групповых маршрутизаторов и временного отключения старых. Затем будет достаточно постепенно обновлять старые маршрутизаторы и возвращать их обратно в эксплуатацию.

    Предложите решение для вот какой проблемы. Допустим, последний маршрутизатор MLDv1 только что убрали с канала. Но маршрутизаторы MLDv2, находясь в режиме совместимости, получают запросы MLDv1 друг от друга и потому пребывают в уверенности, что режим совместимости по-прежнему необходим. Могут ли маршрутизаторы MLDv2 самостоятельно выйти из режима совместимости, а если да, то как? (RFC предлагает ручную настройку.)

    Сводная таблица совместимости между сторонами MLDv1 и MLDv2 будет такой, как показано в Табл. 8.10.

    Совместимость между сторонами MLD: сообщения какой версии использовать?
    Хосты $$\downarrow$$ \ Маршрутизаторы $$\to$$ Только v1 Только v2 v1 и v2
    Только v1 v1 Запросы v2, отчеты v1 v1
    Только v2 v1 v2 v1
    v1 и v2 v1 Запросы v2, отчеты v1 и v2 v1

    А вот сравнительная таблица главных характеристик этих протоколов — Табл. 8.11.

    Сравнение возможностей MLDv1 и MLDv2
    MLDv1 MLDv2
    Типы сообщений ICMPv6 "запрос MLD" (130), "отчет MLD" (131), "итог MLD" (132) "запрос MLD" (130), "отчет MLDv2" (143)
    Виды запроса Общий и ограниченный группой Общий; ограниченный группой; ограниченный группой и источником
    Форма отчета Одна группа на сообщение Несколько групп на сообщение; каждую группу сопровождает режим фильтрации и список источников
    Адрес назначения в отчете и итоге Объявляемая группа "Все маршрутизаторы MLDv2"
    Выборы генератора запросов По адресу IP По адресу IP
    Подавление избыточных отчетов Да Упразднено
    Поддержка SSM и SFM Нет Да
    Кодирование интервала таймера Целочисленное С плавающей точкой, обратно совместимое

    Последним аспектом MLD, который мы не можем обойти вниманием, будет безопасность [§10 RFC 3810]. В какой степени этот протокол защищен от атак, и, с другой стороны, насколько опасны эти атаки? В отличие от ND, состояние стороны MLD зависит только от принятых ею сообщений MLD и не зависит от других внешних событий.

    Напротив, состояние ND может зависеть от любых пакетов IPv6. Так, маршрутизатор обращается к модулю ND по приходу каждого транзитного пакета.

    Поэтому единственный способ атаковать MLD — это подделка сообщений MLD. Главной мерой защиты от такой подделки будет изоляция канала от любых сообщений MLD извне. Действительно, по своему устройству MLD — стопроцентно внутриканальный протокол. Здесь можно пустить в ход весь арсенал доступных средств. Прежде всего, на выходе адрес источника сообщения MLD обязан быть внутриканальным за тем единственным исключением, что в отчете он может быть неопределенным. Тем не менее, отчеты с неопределенным адресом источника необходимы только для подготовки коммутаторов ЛВС на стадии SLAAC (§5.4.2) или DAD (§5.4.1), тогда как узлы IPv6 вполне могут игнорировать их на входе [§5.2.13 RFC 3810]. Действительно, сообщения ND, из которых состоят SLAAC и DAD, все равно не подлежат маршрутизации, и маршрутизаторам нет дела до членства слушателей в группах искомого узла и группе "все узлы канала". А позднее, когда узел назначит себе внутриканальный адрес, он повторит свои отчеты уже с определенным адресом источника. Поэтому на входе адрес источника в сообщении MLD должен быть внутриканальным, а иначе такое сообщение надо просто игнорировать.

    Далее, все сообщения MLD несут опцию "сигнал маршрутизатору", и это существенно облегчает выделение этих пакетов из общего потока. Следовательно, на входе все узлы обязаны убедиться, что пакет MLD снабжен этой опцией.

    Маршрутизаторам к тому же запрещено продвигать пакеты MLD куда бы то ни было. Продвижение MLD в другие каналы запрещено из соображений безопасности, а в тот же самый канал — ради устойчивости сети. Как мы уже отмечали, зацикливание группового трафика может иметь лавинный характер, когда каждая копия пакета рождает несколько копий следующего поколения и т.д. По этой причине MLD не защищают при помощи GTSM, а наоборот, устанавливают в пакетах MLD "предельное число шагов" равным 1. Ведь продвижение пакетов MLD в другие каналы уже заблокировано зонной архитектурой, тогда как продвижение в тот же самый канал она вполне допускает, как было сказано в §2.4.

    Справившись с внешними врагами, мы можем перейти к врагам внутриканальным, а они, конечно, более опасны. Главная угроза исходит здесь от подделки или просто несанкционированной отправки запросов MLD. Для начала злоумышленник способен провести атаку на выборы генератора. Для этого ему достаточно вести передачу запросов с как можно меньшего внутриканального адреса, например, FE80:: или FE80::1. Заполучив роль генератора запросов, или параллельно с этой атакой, злоумышленник может подавить всякую маршрутизацию группового трафика в данный канал. Для этого ему достаточно периодически высылать запрос, ограниченный никому не интересной группой. Как нетрудно убедиться, в этом случае законные маршрутизаторы будут вечно пребывать в состоянии пассивных наблюдателей, а слушатели никогда не получат повод послать свои отчеты. В результате законные маршрутизаторы будут обманным способом убеждены в том, что никаких слушателей на канале просто нет.

    На первый взгляд, такой атаке можно противопоставить модифицированную процедуру выборов, когда таймер OQPT перезапускается только по общему запросу. Однако у злодея в запасе есть ответный ход: слать общий запрос по адресу "все маршрутизаторы MLDv2", FF02::16. Маршрутизаторы обязаны принять и обработать такой запрос, как обычно [§5.1.15 RFC 3810]. В ответ злодею представим себе, что мы внесли поправку в протокол, и теперь общий запрос обязан быть адресован группе "все узлы", FF02::1. Тогда злодей разыграет козырь и пошлет такой запрос инкапсулированным в кадр Ethernet, где адрес назначения MAC равен 33-33-00-00-00-16. Благодаря фильтрации группового трафика на канальном уровне, в коммутируемой ЛВС такой кадр с большой вероятностью попадет только к маршрутизаторам MLDv2 Marc "vanHauser" Heuse. Recent advances in IPv6 insecurities. 27th Chaos Communication Congress. Berlin, 2010. http://events.ccc.de/congress/2010/Fahrplan/events/3957.en.html .

    Практический рецепт против такой атаки — это не принимать пакеты с адреса FE80:: , потому что это зарезервированный в §6.1 адрес anycast, и вручную назначить законным маршрутизаторам наименьшие внутриканальные адреса: FE80::1, FE80::2 и т.д. Однако у злодея снова появится шанс, как только маршрутизатор FE80::1 выйдет из строя, например, из-за атаки типа "отказ в обслуживании".

    В стратегической перспективе, достойный ответ атакам на MLD — это сертификация групповых маршрутизаторов аналогично SEND из §6.3.

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

    Другое дело, что бесконтрольное вступление хостов в группы небезопасно и может привести, например, к утечке конфиденциального трафика. Чтобы управлять членством в группах, понадобится инфраструктура аутентификации и авторизации слушателей MLD. Альтернативное решение проблемы состоит в сквозной защите группового трафика [RFC 3740], а это лучше отвечает фундаментальному принципу TCP/IP: не рассчитывай на помощь транзитных узлов, если без нее можно обойтись.

    Небезопасна и передача трафика группе, которую может вести любой источник. Режимы SFM и SSM сами по себе не делают ее безопасной, так как адрес источника можно подделать. Здесь тоже поможет система сквозной защиты, этакий "групповой IPsec".

    Страницы:

    "Ешь, кума, девятую шанежку — я ведь не считаю". (Пословица)

    Между групповым вещанием в IPv4 и IPv6 нет принципиальных отличий благодаря целенаправленной конвергенции их линий развития. Поэтому, даже если читатель не до конца знаком со всеми механизмами группового вещания IPv4, данный раздел не вызовет у него затруднений, потому что он не опирается на знания из IPv4. Как раз наоборот, у такого читателя будет возможность наверстать отставание в данной теме сразу для обеих версий IP.

    С приходом IPv6 групповое вещание больше нельзя считать второстепенным механизмом и обходить его вниманием даже при начальном изучении темы — ведь без него IPv6 практически неработоспособен.

    "Конструируя" такой важный механизм IPv6, как проколы ND, мы активно применяли групповое вещание, практически не задумываясь над тем, готово ли оно служить нам без каких-либо усилий с нашей стороны. Дабы потом не обнаружилось, что мы построили замок на песке, нам надо уделить немного внимания нашему верному помощнику и убедиться, что у него есть все необходимое для полноценной работы.

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

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

  • В первой из них канальный уровень поддерживает групповое вещание, по крайней мере, в теории. К примеру, идеальная ЛВС Ethernet готова разослать групповой кадр, как только мы укажем верный адрес назначения MAC, в котором установлен бит I/G. На практике же необходимы различные оговорки. Пока сеть Ethernet реализована как общая шина, групповой кадр попадает на вход ко всем станциям, и задача фильтрации группового трафика ложится на сетевые адаптеры. Это означает, что аппаратные фильтры надо явным образом программировать, а когда число групп превышает возможности фильтра, то остается только отключить фильтр. В коммутируемой сети Ethernet проблема неизбирательной доставки групповых кадров по-прежнему остается, потому что коммутаторы заранее не знают, к каким портам подключены члены данной группы. Чуть позже мы посмотрим, как ее можно решить и чего это будет нам стоить.
  • Во второй группе находятся широковещательные канальные технологии без групповой адресации. Типичный представитель этой группы — ныне вышедшая из употребления ЛВС ARCNET. У каждой станции ARCNET был свой канальный адрес длиной 8 бит. Кроме того, нулевой адрес был широковещательным. Однако групповых адресов канального уровня у ARCNET вообще не было. Поэтому групповые адреса сетевого уровня оставалось транслировать в широковещательный адрес ARCNET. Вполне естественно, что эта группа канальных технологий тоже страдает от неизбирательной доставки группового трафика.
  • Третью группу составляют каналы "точка-точка", в которых вся адресация — неявная, например, PPP. Когда локальный узел передает пакет в такой канал, он тем самым как бы заявляет: "Пакет адресован не мне". Канальный уровень, за неимением других вариантов, делает такой вывод: "Раз не мне, то удаленному узлу". И снова групповой пакет попадает на вход узлу, членство которого в группе возможно, но далеко не гарантировано.
  • Наконец, четвертая группа — это каналы NBMA, в которых полноценное групповое вещание невозможно (см. §5.2). Точнее, в них возможно только ограниченное групповое вещание, когда узел рассматривает канал NBMA как множество каналов "точка-точка" и передает групповой пакет всем своим непосредственным соседям. То, что некоторые узлы канала вообще не получат пакет, хотя они, может быть, и состоят в данной группе, — проблема данного типа каналов, и мы с ней вряд ли что-то можем поделать, не меняя тип канала с NBMA на широковещательный при помощи дополнительного протокола (например, LANE в ATM или VPLS в MPLS). В то же время, и проблема неизбирательной доставки тоже остается, потому что узел-источник не обладает сведениями о членстве соседей в данной группе. Хотя такой режим вещания можно назвать групповым только условно, он может пригодиться, например, хостам для розыска маршрутизатора по умолчанию, так как этот маршрутизатор — всегда сосед хоста (см. §5.2).
  • Сухой остаток из этого сравнения таков: если канал вообще поддерживает групповое вещание, то он сделает все возможное, чтобы доставить групповой пакет всем членам группы, но он вправе пожертвовать избирательностью, доставив пакет и узлам, в данной группе не состоящим. Поэтому узел обязан фильтровать входящий групповой трафик по адресу назначения на всех доступных ему уровнях стека. Так как возможность фильтровать его на канальном уровне ограничена, в основном из-за рудиментарной групповой адресации, главная доля ответственности ложится здесь на сетевой уровень, то есть IP.

    Мы сформулировали это требование фильтрации пакетов на входе для группового трафика, потому что именно о нем сейчас идет речь. Однако на практике то же самое требование справедливо и для индивидуального трафика. Так, хост не должен полагать, что любой входящий пакет адресован именно ему. Хост может получать чужие пакеты по ошибке, например, из-за неправильных или устаревших маршрутов в сети. Требования устойчивости и безопасности в этом случае очевидны: прежде чем принять пакет в обработку, хост должен убедиться, что адрес назначения пакета — действительно его. Приведем аналогию из обычной жизни: перед тем как вскрыть найденный в почтовом ящике конверт, хорошим тоном будет проверить адрес получателя.

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

    Строго говоря, нет абсолютной гарантии, что внутри принятого группового кадра окажется именно групповой, а не индивидуальный пакет. Как мы упоминали в примечании к §5.2, некоторые семейства протоколов за пределами TCP/IP даже пользуются этим трюком, чтобы сэкономить на явном разрешении индивидуальных адресов. К их числу относится, например, ISO.

    В то же время, обратная операция может потребовать дополнительных усилий, если канальный уровень поддерживает явную адресацию. Какой канальный адрес назначения мы укажем в групповом кадре? Как раз здесь нам пригодятся правила разрешения групповых адресов IPv6 для данного типа канальной инкапсуляции, а они заведомо локальны и сводятся к какому-то вычислению (cм. §4.1.1). Скажем, в ARCNET любой групповой адрес IPv6 превратится в адрес 0 [§7 RFC 2497], а в Ethernet это будет полученный подстановкой битов адрес 33 33 XX XX XX XX [§7 RFC 2464]. Хотя даже в последнем случае отображение адресов далеко от взаимной однозначности, это не вызовет проблем благодаря обязательной фильтрации на входе.

    На этот аспект можно посмотреть и с другой стороны: потеря информации при разрешении групповых адресов IP в канальные адреса — еще одна весомая причина фильтровать входящие групповые пакеты по их адресу назначения. Это касается не только IPv6, но и IPv4. Так, групповой адрес IPv4 тоже отображается с потерей битов, например, в тот же нулевой адрес ARCNET [§4.3 RFC 1201] или в адрес MAC 48 01-00-5E-XX-XX-XX [§6.4 RFC 1112].

    Наконец мы снова вынырнули на сетевой уровень. Здесь задача группового вещания IPv6 сводится к трем очевидным подзадачам, которые уже знакомы нам по индивидуальной передаче:

  • передача в пределах канала;
  • прием;
  • маршрутизация.
  • С передачей по каналу мы уже практически разобрались. Передающий узел (источник или маршрутизатор) выбирает по каким-то правилам выходной сетевой интерфейс, а затем просто отдает групповой пакет в ведение соответствующего канального модуля. Тот, если нужно, вычисляет канальный адрес назначения и — в добрый путь!

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

    В групповом вещании IP передача и прием — операции независимые, так что вещать группе вполне может узел, в ней не состоящий. Членство в группе влияет только на прием.

    Прекрасно, групповое вещание IPv6 по каналу уже работает. Теперь попробуем выйти за пределы одного канала и подумаем над маршрутизацией групповых пакетов. Допустим, у группы есть члены за пределами канала, к которому подключен источник. Понадобится ли источнику таблица групповых маршрутов, или хотя бы групповой маршрутизатор по умолчанию? Придется ли ему отдельно работать с маршрутизатором и членами группы на канале? На самом деле, всех этих сложностей можно избежать, если маршрутизатор сам притворится одним из членов группы и станет принимать групповой трафик наравне с ними (см. рис. 8.1), вместо того чтобы служить явным следующим шагом (next hop) групповых пакетов. Поскольку маршрутизатор на самом деле не потребляет принятые групповые пакеты сам, а продвигает их дальше, признать его полноценным членом группы мы не можем. Он — только слушатель (listener) данной группы. Действительные члены группы — тоже ее слушатели, однако не все слушатели группы — ее члены.

    (рис 8.1) С точки зрения источника, групповые маршрутизаторы неотличимы от хостов

    Чтобы такая схема работала, групповой маршрутизатор должен каким-то образом узнать, какие группы ему слушать и куда их продвигать. Конечно, эти сведения можно сообщить ему путем ручной настройки, но это плохо вяжется с динамическим характером группового вещания. Допустим, маршрутизатор соединяет несколько каналов: К1, К2, К3,… КN. Если текущий источник группы Г находится на канале К1, а слушатели есть только на К2, то маршрутизатор должен слушать группу Г, как минимум, на К1 и продвигать ее трафик в К2. Самое примитивное решение могло бы состоять в том, чтобы слушать группу Г на всех N каналах, а продвигать групповой пакет в N - 1 каналов, за исключением того, откуда пришел данный пакет. Это напоминает нам процесс лавинной рассылки (flooding) в работе коммутатора ЛВС.

    Возможно ли оптимизировать этот процесс? Как мы помним, обучаемый коммутатор ЛВС извлекает информацию о местонахождении узла из того, через какой интерфейс (порт) приходят от него кадры, в предположении, что путь к данному узлу совпадает с путем от него. Поэтому коммутатор делает, к примеру, такой вывод: "Если кадр от источника У2 пришел через интерфейс И5, то впредь я стану продвигать кадры, адресованные У2, только в интерфейс И5". Но, увы, сейчас нам от этого трюка нет никакой пользы. Ведь входной интерфейс группового пакета не дает маршрутизатору никаких сведений о том, где находятся слушатели группы. Корень проблемы здесь кроется в том, что слушатели никак себя не проявляют, а косвенных признаков явно недостаточно. Во-первых, источник группового пакета — далеко не всегда слушатель этой группы. Во-вторых, групповой адрес никогда не возникнет в поле "источник", потому что архитектура IP не допускает коллективного авторства пакетов. В-третьих, не все слушатели группы — ее члены и могут говорить от имени группы.

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

    Чтобы восполнить этот недостаток информации, слушателям группы придется явным образом заявить о себе, а для этого им понадобится соответствующий протокол. Так как это будет очередной аспект управления IPv6, мы поместим его в удобные рамки ICMPv6 и назовем розыск групповых слушателей (Multicast Listener Discovery, MLD).

    В то же время, источники группового трафика обнаружат себя, передавая пакеты, и дополнительный протокол их розыска не требуется. Групповому маршрутизатору достаточно настроить свои активные сетевые интерфейсы так, чтобы они принимали все групповые пакеты. Например, пока маршрутизатор работает только с IPv6 и Ethernet, ему достаточно принимать все кадры MAC, у которых адрес назначения начинается на 33 33. Такой теневой прием всего группового трафика еще не делает маршрутизатор слушателем всех возможных групп. Групповой маршрутизатор слушает группу только тогда, когда он готов продвигать для нее трафик. Это различие важно, так как настоящий слушатель должен заявить о себе с помощью MLD, а не только втихомолку принимать пакеты.

    Аналог MLD в IPv4 — это протокол IGMP. Его современные версии основаны на точно тех же идеях, а сообщения IGMP играют те же роли, что сообщения MLD. Более того, версии IGMP и MLD фактически синхронизированы, поскольку групповое вещание IPv4 и IPv6 развивается параллельно. Так, IGMPv2 отвечает MLDv1, а IGMPv3 — MLDv2. Что скрывается за этими номерами версий, мы сейчас увидим.

    Работу над MLD мы начнем с того, что уточним, какой объем сведений о слушателях группы Г на канале К действительно необходим маршрутизатору. Будет ли это поименный список? Когда маршрутизатор продвигает групповой пакет в канал К, он передает в этот канал ровно одну копию пакета, а всю остальную работу делают канальные механизмы. Поэтому на самом деле групповому маршрутизатору достаточно знать, есть ли вообще слушатели группы Г на данном канале К, а их число и состав совершенно неважны. Это простое соображение и послужит нашей отправной точкой.

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

    Следующее очевидное соображение состоит в том, что областью распространения MLD будет один канал. Поэтому адресам источника и назначения в сообщениях MLD прекрасно подойдет внутриканальная область. Кроме того, надежность передачи пакетов в пределах канала обычно удовлетворительная, и нам можно не обременять себя и протокол средствами гарантированной доставки данных, а будет достаточно повторять сообщения время от времени.

    Любителям истории TCP/IP будет небезынтересно отметить, что самая ранняя версия IGMP [Приложение I RFC 988], условно известная как IGMPv0, как раз обеспечивала гарантированную доставку сообщений от слушателя к маршрутизатору. На практике оказалось, что без этого можно обойтись и тем упростить реализацию сторон протокола. Ведь гарантированная доставка неизбежно требует хранить историю передачи, тогда как периодический повтор вполне обходится без сохраненного состояния.

    В первом приближении, наша пробная версия MLD могла бы работать следующим образом. Слушатели шлют отчеты (Report) о принимаемых группах, просто перечисляя адреса этих групп. Маршрутизатор вызывает отчеты слушателей, направив в канал явный запрос (Query), хотя слушатель может выслать отчет и по собственной инициативе. Чтобы на первых порах не заботиться о размере отчета в контексте MTU, пусть каждый отчет содержит ровно один групповой адрес. Если слушатель принимает несколько групп, то он вышлет по отдельному отчету о каждой из них. Кто в этой схеме будет задавать ритм? Пока на канале нет группового маршрутизатора, в периодических отчетах смысла тоже нет. Поэтому пусть именно маршрутизатор периодически высылает запрос, а слушатели отвечают на него.

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

    Тем не менее, в одном случае оправдан немедленный и добровольный отчет о группе, а именно когда новый слушатель начинает принимать данную группу. Вот нам еще одно важное действие хоста при вступлении в группу Г на интерфейсе И: направить отчет MLD о группе Г в интерфейс И. Чтобы отчет не потерялся, его стоит повторить несколько раз.

    Какой адрес источника IPv6 будет указан в пакете с отчетом? Если у интерфейса И уже есть внутриканальный адрес, то надо использовать его. Однако на момент вступления в группу Г интерфейс И может быть еще без адресов, например, если группа Г — это группа искомого узла, которая отвечает пробному внутриканальному адресу интерфейса И (см. процедуру DAD в §5.4.1). Выходит, чтобы вступить в группу, нужен адрес, а чтобы назначить адрес, нужна группа? Это типичная "проблема курицы и яйца", для которой у нас уже есть типовое решение: в пакете с отчетом MLD допустим неопределенный адрес источника :: [RFC 3590, §5.2.13 RFC 3810]. Это вполне приемлемо для MLD, поскольку маршрутизатор все равно не ведет поименного списка слушателей группы.

    Все группы искомого узла — внутриканальные. Тогда зачем их вообще объявлять по MLD? Как станет видно через пару абзацев, это весьма резонный вопрос, располагающий к небольшой беседе.

    В такой схеме работы MLD запросу не нужны никакие дополнительные параметры, а отчет содержит всего одно информативное поле, адрес группы. Но как маршрутизатор определит, что у группы Г не осталось слушателей, когда все они прекратят ее прием? В этом случае запрос маршрутизатора не вызовет ни одного отчета об этой группе. Так как запросы и отчеты все же могут теряться, маршрутизатору не следует спешить и надо подождать еще несколько запросов подряд. А уж если и в ответ на них не пришло ни одного отчета, в котором адрес группы равен Г, то значит, ее и правда больше никто не слушает.

    Наконец, нам следует определить, по какому адресу надо слать запрос и отчет. Запрос должен достичь всех узлов канала, так как заранее неизвестно, кто из них групповой слушатель. Поэтому запрос направляется группе "все узлы канала", FF02::1. Чтобы при этом не возникло очередной "проблемы курицы и яйца", пусть эта группа будет особенной: она никогда не объявляется по MLD. Так как она внутриканальная, то и маршрутизации она не подлежит.

    На первый взгляд, можно было бы назначить особый групповой адрес "все слушатели канала". Однако групповое вещание в IPv6 — обязательная функция, которую в любом случае применяет протокол ND (§5), так что выигрыша от такого назначения не было бы вовсе, потому что любой интерфейс IPv6 — член нескольких групп. (Перечислите, каких.)

    Маршрутизатор вправе продвинуть индивидуальный пакет в тот же канал, откуда он пришел, даже если адрес назначения в нем внутриканальный; ведь зонная архитектура IPv6 вполне допускает такое поведение (см. §2.4). В то же время, продвигать обратно в канал групповой пакет точно не следует, потому что это прямая дорога к зацикливанию трафика. А если групповых маршрутизаторов на канале будет несколько, то может даже возникнуть лавинный эффект, когда число копий пакета растет с каждым циклом экспоненциально. Чтобы понизить вероятность такого сбоя, "предельное число шагов" в пакетах MLD устанавливают равным 1, а значит, не применяют к ним GTSM (§5.1).

    Выходит, если трафик внутриканальных групп все равно не подлежит маршрутизации, то и сведения о них маршрутизатору не нужны. Может, их надо вообще исключить из объявлений MLD? На самом деле, нет. Дело здесь в том, что отчеты о внутриканальных группах интересны интеллектуальным коммутаторам ЛВС, которые заняты"подслушиванием" MLD. До этой темы мы доберемся буквально через несколько абзацев.

    Что касается групп из области интерфейса (FFx1:… ), то они имеют значение только в пределах данного интерфейса, а адресованные им пакеты никогда не покидают пределов узла. Поэтому естественно, что группы из этой области никогда не упоминаются в сообщениях MLD. То же справедливо для групп зарезервированной области 0.

    Что касается отчета, то у нас целых три кандидата на адрес его назначения: индивидуальный адрес маршрутизатора, который был источником запроса, группа "все маршрутизаторы канала" и… группа Г, о которой дается отчет. Первый кандидат, то есть адрес маршрутизатора, плох тем, что отчет не получат другие маршрутизаторы того же канала, когда они есть, а слушателям придется отвечать на запрос каждого маршрутизатора отдельно. Маршрутизаторы у нас уже получают запросы друг друга, и они могли бы выбрать, кто из них служит "метрономом" данного канала (генератор запросов, querier), чтобы понизить суммарную нагрузку. Вопрос о точном механизме этих выборов мы пока отложим в наш "внутренний стек", но сама идея оптимизации довольно прозрачна: только один маршрутизатор запрашивает, но отчеты получают все. Второй кандидат, "все маршрутизаторы канала", допускает такую оптимизацию, но вовлекает в прием отчетов маршрутизаторы индивидуального трафика, которым эти отчеты неинтересны. Наконец, группа Г, о которой дается отчет, позволит сообщению достичь всех групповых маршрутизаторов, потому что они уже ведут теневой прием всех возможных групп на всех своих активных интерфейсах; ведь именно так маршрутизаторы следят за источниками группового трафика. Кроме того, отчет получат другие слушатели группы Г, а это может пригодиться для дополнительной оптимизации трафика MLD. Поэтому пусть отчет о группе Г адресуется этой же самой группе — по крайней мере, в нашей самой первой версии MLD.

    Суть дополнительной оптимизации вот в чем. Раз маршрутизатору неинтересен поименный список слушателей, то о каждой группе достаточно одного отчета на весь канал. Пока слушатель задерживает свой отчет о группе Г на случайное время, он заодно ожидает, не придут ли отчеты о Г от других ее слушателей, у которых задержка оказалась короче. Если такой отчет получен, значит, кто-то другой успел отрапортовать о группе Г первым, и повторный отчет избыточен. Как мы скоро увидим, у этого трюка есть и обратная, отрицательная сторона: коммутаторам сложнее следить за топологией группы в пределах ЛВС.

    На схеме, которую мы наметили под видом "пробной версии MLD", основана работа протокола IGMPv1 в IPv4 [Приложение I RFC 1112]. В нем выборы генератора запросов оставлялись вышестоящему протоколу групповой маршрутизации.

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

    Позднее мы увидим, как прием запросов наряду с отчетами позволяет точнее управлять таймерами протокола. Тем не менее, для правильной интерпретации отчета MLD знать текст запроса совершенно не нужно. Более того, отчет MLD может быть и не по запросу, например, когда слушатель начинает прием новой группы.

    Это свойство MLD открывает нам путь к оптимизации группового трафика не только на сетевом, но и на канальном уровне. Скажем, интеллектуальный коммутатор Ethernet вполне может следить за отчетами MLD, которые входят через его порты, и таким образом вести учет слушателей групп на этих портах. Конечно, тогда коммутатору придется нарушить границу между уровнями в стеке протоколов, потому что он будет сначала выделять пакеты IPv6 с отчетами MLD среди всех возможных кадров Ethernet, а затем анализировать их. Последним шагом будет преобразование групповых адресов IPv6 в групповые адреса MAC, так как коммутатор все же управляет трафиком на основании канальных, а не сетевых адресов. Тем не менее, этот трюк вполне по силам современным коммутаторам, вычислительные возможности которых давно превосходят самые смелые мечты пионеров ЭВМ. Поскольку коммутатор в такой схеме подслушивает чужие сообщения, хотя ему это и не положено по его сетевой роли, данный прием известен как подслушивание MLD (MLD snooping).

    Конечно, тот же самый прием коммутаторы используют и по отношению к IPv4, подслушивая IGMP.

    Обратите внимание, какую роль в подслушивании IGMP и MLD играют правила преобразования групповых адресов IP в групповые адреса MAC. Они сводятся к простой подстановке битов (§4.1.1) и потому не представляют трудности для коммутатора. Иначе коммутатор не смог бы воспользоваться информацией из сообщений IGMP или MLD, потому что не знал бы, какой канальный адрес отвечает данной группе IP.

    Хотя с архитектурной точки зрения подслушивание MLD и IGMP — всего лишь сомнительный трюк, на практике оно стало самым популярным подходом к управлению групповым трафиком IP на канальном уровне. Даже общепринятые протоколы вынуждены считаться с ним. В частности, слушатели обязаны объявлять свои внутриканальные группы, кроме FF02::1 и 224.0.0.1, и это явная дань подслушиванию MLD и IGMP.

    Предложите способ управления на канальном уровне группами "все узлы IP", FF02::1 и 224.0.0.1, при том что они не объявляются слушателями. Иными словами, по каким признакам коммутатор может своевременно установить, что на данном порту есть хотя бы один узел IPv4 или IPv6? (Подсказка: ARP с протоколом 0x800 или BOOTP/DHCPv4 — это IPv4; ND или DHCPv6 — это IPv6.)

    Получив работоспособный прототип MLD, мы можем заняться его оптимизацией. Прежде всего, обратим внимание на одну серьезную проблему, которую мы сами же и создали. Когда слушатель группы Г направляет отчет MLD по адресу Г, он рассчитывает, что этот отчет примут и обработают все групповые маршрутизаторы канала, а не только слушатели группы Г. Это различие важно, например, когда группа Г впервые появляется на данном канале и маршрутизаторы о ней еще ничего не знают. Чтобы эта схема сработала, маршрутизаторам будет недостаточно настроить свои канальные фильтры на прием всех групповых пакетов, — им также придется вести анализ всех входящих групповых пакетов, и анализ этот будет более глубоким и трудоемким, чем того требует обычная адресация IPv6. Ведь до сих пор узел, будь то хост или маршрутизатор, заглядывал дальше основного заголовка IPv6 только в том случае, если адрес назначения IPv6 в основном заголовке пакета принадлежал узлу.

    В IPv4 это было, строго говоря, не так, поскольку маршрутизатор просматривал все опции IP.

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

    Обычно, если пакет был индивидуальный, его адрес назначения должен был быть назначен одному из интерфейсов узла; если же пакет был групповой, то узел должен был быть членом данной группы на входном интерфейсе пакета. В противном случае пакет был чужим; хост отбраковывал его, а маршрутизатор продвигал согласно текущей политике. Этот простой критерий "свой/чужой" позволял эффективно выделять пакеты, требующие анализа, из всего трафика, что особенно важно для маршрутизаторов. Теперь же мы фактически обязали групповые маршрутизаторы полностью разбирать всю цепочку заголовков IPv6 в каждом входящем групповом пакете, чтобы выделить из общего потока сообщения ICMPv6, а затем анализировать заголовок ICMPv6, чтобы отделить и обработать интересные маршрутизатору сообщения MLD.

    Вспомним важный факт, который мы узнали из §3.1: в отличие от IPv4, тип полезной нагрузки пакета IPv6 не всегда можно определить прямо из его основного заголовка.

    Простой, но действенный прием против этой проблемы сводится к тому, чтобы заранее помечать пакеты MLD. Делать это должен, конечно же, их источник, но как? Среди всех значений "следующий заголовок" в §3.3.2 мы особо выделили одно, а именно нулевое. Это значение отвечает заголовку пошаговых опций, представляющих интерес для всех узлов по пути пакета. Его особенность в том, что встретиться оно может только в основном заголовке IPv6 ради легкого доступа к пошаговым опциям.

    Почему бы нам не воспользоваться этим механизмом для маркировки служебных пакетов, нарушающих правила адресации? Ведь на самом деле маршрутизаторы IPv6 уже проверяют, нет ли в транзитном пакете пошаговых опций, а стоимость этой проверки низка, потому что ограничена основным заголовком пакета. Просто до сих пор у нас не возникало задач, где бы это пригодилось. Итак, пришло время "сконструировать" пошаговую опцию, которая скажет маршрутизатору: обрати внимание на этот пакет, он может содержать интересные тебе сведения, хотя он и не адресован тебе явным образом. За свою роль эта опция называется "сигнал маршрутизатору" (Router Alert) [RFC 2711].

    Сначала нам надо выбрать численный тип этой опции. Как мы помним из §3.3.2, три старших бита в нем отражают свойства опции. Первое свойство — это можно ли игнорировать опцию, если узел не поддерживает ее. В данном случае это так. Второе свойство — неизменность. У транзитных узлов нет никакой причины изменять значение данной опции, поэтому она будет неизменной. Такой комбинации свойств отвечают три нулевых бита, 000, в старших разрядах типа опции, а значит, значение надо выбрать из диапазона от 0 до 31. Общепринятый тип этой опции — 5.

    Какие данные будет содержать эта опция, и нужны ли они ей вообще? Хотя само ее присутствие — это уже сигнал для маршрутизатора, давайте оптимизируем наш подход и точнее укажем тип сведений, которые маршрутизатор сможет извлечь из данного пакета (см. рис. 8.2). По сути, это классификация протоколов под несколько другим углом зрения. Если протоколы IP (значения для поля "следующий заголовок") делают ударение на инкапсуляции, то сейчас мы перенесем его на способы управления трафиком. Каждый протокол управления получит свой код из соответствующего реестра http://www.iana.org/assignments/ipv6-routeralert-values [ ], и тогда маршрутизатор сможет быстро узнать, интересен ли ему этот пакет, без того чтобы разбирать всю цепочку заголовков. Протокол MLD пришел за своим кодом самым первым и потому получил почетное нулевое значение. Для простоты и определенности пусть все сообщения MLD несут пошаговую опцию "сигнал маршрутизатору" с кодом 0 внутри.

    Чтобы проще объяснить, зачем понадобился еще один реестр протоколов, применим кулинарную параллель: опция "сигнал маршрутизатору" не только выделяет пирожки с начинкой среди обычных булочек, но и говорит, какая там начинка внутри. Ведь гурману важен не способ начинки, а ее вкус.

    Обсудите, как применение опции "сигнал маршрутизатору" влияет на устойчивость и безопасность сети, а также что этому можно противопоставить [§4 RFC 2711].

    (рис 8.2) Пошаговая опция IPv6 "сигнал маршрутизатору"

    Поле кода внутри опции "сигнал маршрутизатору" — длиной 2 байта. Как читатель уточнит его спецификацию, чтобы она стала однозначной? (Ответ: способ кодирования целый беззнаковый, порядок байтов сетевой.)

    Упражнение для читателя: составить заголовок пошаговых опций, содержащий только опцию "сигнал маршрутизатору" с кодом MLD, и заполнить все поля. Сможете ли вы это сделать? Если нет, то как обойти возникшее затруднение? (Ответ: придется добавить два Pad1 или один пустой PadN.)

    Перейдем к следующему усовершенствованию. В предварительной версии MLD мы отложили вопрос о том, как групповые маршрутизаторы канала выберут ровно одного из своего числа на роль генератора запросов ( querier). Вот простой и остроумный подход к этой, казалось бы, сложной задаче: пускай роль генератора играет маршрутизатор с наименьшим адресом IP.

    Мы, конечно же, помним из §2.2, что адрес IP (v4 или v6) — это цепочка битов с определенным порядком старшинства, а значит, ее можно рассматривать как двоичную запись целого неотрицательного числа. Однозначность позиционной записи чисел гарантирует нам, что соответствие между побитовым представлением адреса и его численным значением взаимно однозначно, а значит, у разных адресов заведомо разное численное значение. Точнее говоря, позиционная запись однозначна с точностью до незначащих нулей, но адресов IP эта оговорка не касается ввиду их фиксированной разрядности. Если бы адреса IP были переменной длины, нам пришлось бы явным образом уточнить, имеют ли значение нули в старших разрядах адреса, например, являются ли 1 и 01 суть разными адресами или же эквивалентными представлениями одного и того же адреса. Разумеется, все эти соображения справедливы только в пределах определенной версии IP.

    Чуть выше мы позаботились, чтобы маршрутизаторы получали запросы друг друга. Каждый из них шлет свои запросы с определенного внутриканального адреса, который, несомненно, уникален в пределах канала — об этом позаботился механизм DAD из §5.4.1. Теперь представим себе, что маршрутизатор получил запрос, адрес IP источника в котором численно меньше, чем его собственный. Это значит, что есть более вероятный претендент на роль генератора, и собственные запросы надо приостановить на какое-то время, большее, чем стандартный период повтора запросов. В результате самый подходящий кандидат станет непрерывно подавлять своими запросами другие маршрутизаторы, и те будут оставаться пассивными наблюдателями (non-querier). Математическая основа этого трюка — транзитивность отношений "больше" и "меньше" на множестве целых чисел: $$A>B, B>C\Rightarrow A>C$$. Благодаря этому свойству процесс выборов гарантированно сойдется, как только все маршрутизаторы получат все неподавленные запросы своих соперников. Когда же текущий генератор выйдет из игры, сработают таймеры, процедура выборов повторится, и место генератора займет новый победитель. В результате мы получаем простой и устойчивый механизм.

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

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

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

    Чтобы ускорить этот процесс, пусть слушатель явным образом извещает маршрутизаторы о прекращении приема данной группы Г. Для этого он высылает сообщение нового типа, итог (Done). Как и отчет, это сообщение содержит в себе адрес группы Г, о которой идет речь. По какому адресу лучше всего направить итог? Здесь у нас пока всего два варианта, "все маршрутизаторы канала", FF02::2, и адрес данной группы Г. Обычно маршрутизаторов на канале меньше, чем потенциальных слушателей группы, а сообщения типа "итог" будут относительно редки, так что мы остановим свой выбор на первом варианте, FF02::2.

    Теперь представим себе, что групповые маршрутизаторы канала получили итоговое сообщение о группе Г от некоего слушателя. Поскольку маршрутизаторы не ведут поименный список слушателей и даже не считают их "по головам", то само по себе это сообщение еще не дает им всей необходимой информации — оно только извещает, что произошли изменения в числе слушателей группы Г. Естественной реакцией маршрутизаторов будет проверить, остались ли в группе Г еще слушатели. Для этого генератору запросов надо послать в канал несколько запросов с короткими паузами между ними.

    Если это будут обычные запросы, они вызовут целый поток информации, не относящейся к делу. Поэтому нам следует ограничить эти запросы группой Г, о которой пришло итоговое сообщение. Во-первых, такие запросы надо направлять по адресу группы Г, а не "все узлы канала". Во-вторых, в самих запросах надо указать, что они касаются только группы Г. То есть тело запроса тоже должно включать в себя поле "адрес группы". В обычном, общем запросе (General Query) значение этого поля будет нулевым, тогда как в запросе, ограниченном группой (Multicast-Address-Specific Query), это поле содержит действительный адрес группы.

    Мелкая, но важная деталь — это взаимодействие между процедурой быстрой проверки группы и выборами генератора запросов. Как быть текущему генератору, если он еще не послал положенное число запросов группе Г, а тем временем пришел чужой запрос с еще меньшим адресом источника? Пусть ради простоты и устойчивости протокола текущий генератор всегда доводит процедуру быстрой проверки до конца. Ведь выборы генератора — это лишь оптимизация, и не будет никакой беды в том, что на протяжении нескольких секунд запросы будут слать два маршрутизатора.

    Последний штрих в нашей схеме быстрой проверки группы коснется координации генератора запросов с наблюдателями. Как последние узнают, что идет оперативная проверка группы и связанные с ней таймеры надо установить на более короткий интервал? По нашему плану, наблюдатели продолжают получать запросы, а ритм задает генератор запросов, так что пускай он заодно сообщает наблюдателям, какой тайм-аут надо связать с данным запросом. Для этого достаточно одного целочисленного поля в формате запроса. В свою очередь, слушателям оно пригодится, чтобы равномернее распределить отчеты в доступном времени, выбирая случайную задержку от нуля до значения этого поля. За эту роль данное поле известно как MRD (Maximum Response Delay, максимальная задержка отклика). Размерность значения MRD — миллисекунды.

    Маршрутизаторы обращают внимание на поле MRD, только если принят запрос, ограниченный группой. Общие запросы на интервалы таймеров не влияют.

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

    Перейдем к формату сообщений. Формат запроса и ответа будет одним и тем же, чтобы упростить реализацию (см. рис. 8.3). Конечно, поле(рис 8.3) Сообщение MLDv1

    "Сконструированные" нами механизмы составляют костяк первой версии протокола розыска слушателей IPv6, MLDv1 [RFC 2710].

    Тем же самым образом работает IGMPv2 в IPv4 [RFC 2236].

    Любопытно отметить, что у сообщений MLDv1 типы численно меньше, чем у сообщений ND. Это можно объяснить тем, что MLD логически предшествует ND. К примеру, группу искомого узла надо объявить по MLD до того, как ею можно пользоваться в целях ND.

    Мы провели довольно много времени, пересматривая и оттачивая детали протокола MLDv1, так что давайте соберем самые главные его черты в одной таблице, Табл. 8.1, а заодно сравним его с нашей пробной "нулевой версией", аналогичной IGMPv1 в IPv4, чтобы лучше увидеть проделанный путь.

    Сравнение возможностей "MLDv0" и MLDv1
    "MLDv0" MLDv1
    Типы сообщений ICMPv6 "запрос MLD" (130), "отчет MLD" (131) "запрос MLD" (130), "отчет MLD" (131), "итог MLD" (132)
    Виды запроса Только общий Общий и ограниченный группой
    Форма отчета Одна группа на сообщение Одна группа на сообщение
    Адрес назначения в отчете Объявляемая группа Объявляемая группа
    Выборы генератора запросов Механизм не определен По адресу IPv6
    Подавление избыточных отчетов Да Да
    Управление интервалом таймера Нет Да

    Способны ли мы еще улучшить наш результат во второй версии протокола, MLDv2 [RFC 3810]? Конечно! Мы даже готовы немедленно выступить с черновым списком усовершенствований:

  • Центральным объектом MLDv1 выступает группа. В частности, сообщения MLDv1 относятся к одной группе (или ко всем группам сразу), а механизм подавления избыточных отчетов достигает того, что о каждой группе дается один отчет. Следовательно, накладные расходы на работу такого протокола растут пропорционально числу активных групп, а оно может быть велико, особенно если на канале в основном расположены маршрутизаторы, каждый из которых продвигает трафик многих разных групп. Напротив, число слушателей на канале вряд ли будет очень большим из чисто практических соображений. Поэтому имеет смысл перенести фокус протокола с группы на слушателя. Для начала надо переработать формат отчета так, чтобы он смог нести информацию о многих группах сразу.
  • MLDv1 поддерживает только один режим группового вещания, а именно ASM. В новой версии MLD совершенно необходима поддержка SSM и SFM. О различиях между этими режимами см. §2.9.
  • В MLDv1 механизм координации генератора запросов с наблюдателями находится в зачаточном состоянии. Его надо распространить на прочие важные параметры протокола: интервал между запросами, число повторов.
  • Аналогичные усовершенствования вошли в IGMPv3 для IPv4 [RFC 3376].

    Первый пункт мы осуществим, усложнив формат отчета (см. рис. 8.4). Теперь он будет содержать переменное число записей о группах. Однако тогда нам придется пересмотреть адрес назначения отчета MLDv2. В MLDv1 это был адрес группы, о которой дается отчет, поскольку он был ровно один. Когда отчет дается о переменном числе групп, нам ничего не остается, как адресовать его фиксированной общепринятой группе "все маршрутизаторы с поддержкой MLDv2", FF02::16. Другие слушатели больше не получат этот отчет, а значит, механизм подавления избыточных отчетов работать не сможет. Его ценность уже упала благодаря понижению числа отчетов, так что в MLDv2 мы от него полностью откажемся. Пусть каждый слушатель MLDv2 говорит сам за себя, без оглядки на других. Это заодно упростит реализацию слушателя.

    (рис 8.4) Отчет MLDv2

    Еще один аргумент против подавления избыточных отчетов в MLDv2 связан с подслушиванием MLD, которое ведут интеллектуальные коммутаторы ЛВС [§A.2 RFC 3810]. В простейшем случае такой коммутатор продвигает групповые кадры только в те порты, откуда были замечены отчеты MLD о данной группе. Если же отчет подавлен, то коммутатор решит, что на этом порту слушателей нет, и продвигать групповые кадры туда не станет. Чтобы обойти эту проблему, коммутатор, в свою очередь, подавляет продвижение отчетов MLD в те порты, где, как ему кажется, нет групповых маршрутизаторов [§2.1.1 RFC 4541]. То же справедливо для IGMP. Не стоит сомневаться, что такой трюк только усложняет настройку сети и дестабилизирует ее работу.

    Родственная проблема состоит в том, что коммутатор должен копировать весь групповой трафик в порты, где есть групповые маршрутизаторы, поскольку те ведут теневой прием всех групп, не вступая в них явным образом. В противном случае, например, маршрутизаторы не смогут получать отчеты MLDv1. Увы, коммутатор не может надежно обнаружить групповые маршрутизаторы по их запросам MLD, так как только генератор запросов ведет их постоянную передачу, а наблюдатели молча следят за чужими сообщениями MLD. Поэтому, чтобы коммутатор мог самостоятельно определить, на каких портах есть групповые маршрутизаторы, предложен отдельный простой протокол MRD (Multicast Router Discovery) [RFC 4286].

    Объединение информации о многих группах в один отчет может привести к тому, что полная длина пакета с отчетом превысит MTU канала, а фрагментировать отчет было бы нежелательно. Чтобы обойти и эту трудность, один длинный отчет о множестве групп будет эквивалентен нескольким отчетам поменьше, каждый о подмножестве этого множества, вплоть до одного отчета на группу. Это позволит слушателю разбить длинный список групп на порции подходящего размера, не применяя никаких дополнительных механизмов, а просто управляя числом групп на отчет [§5.2.15 RFC 3810].

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

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

    Для начала заметим, что для маршрутизатора нет принципиальной разницы между SSM и SFM. Это хост знает, что в режиме SSM разные источники S в комбинированном адресе (S,G) означают разные каналы вещания, которые могут принимать совершенно разные приложения, как мы обсудили в §2.9. Маршрутизатор же просто оптимизирует распространение группового трафика. Поэтому, когда хост принимает два канала, $$(S_1,G) и (S_2,G)$$, которые отличаются только адресом источника, но не адресом назначения, маршрутизатору достаточно допустить источники $$S_1 b S_2$$ к группе G, а остальные блокировать, чтобы не расходовать впустую ресурсы сети. То же самый подход применим и к SFM.

    Остаточные особенности фильтрации SSM обсуждаются в [RFC 4604].

    В общем, картина такая: маршрутизатор допускает некое множество адресов источника S к данной группе G, а остальные адреса блокирует. Поэтому нашу модель фильтрации мы выразим на языке элементарной теории множеств, а оперировать она будет только адресами источника, так как у каждого группового адреса назначения будет свой независимый фильтр. Множество разрешенных адресов S мы станем называть первичным, потому что в нашей модели оно составляет истинный смысл фильтра, хотя способы фиксации (кодирования) этой информации могут быть разными.

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

    Множество всех адресов IPv6 — это универсум данной задачи, то есть множество всех возможных значений. Оно конечно, и поэтому теоретически любой фильтр можно записать, просто перечислив адреса источника. Так, режим вещания ASM обеспечивает фильтр, содержащий все адреса IPv6, а полное прекращение приема группы равноценно фильтру, в котором нет ни одного адреса (пустое множество). А когда несколько слушателей выражают свои пожелания касательно фильтрации трафика в виде множеств, маршрутизатору достаточно найти их объединение, чтобы удовлетворить все запросы и не заблокировать ни один желательный источник: S=\bigcup_{i} S_i. На бумаге выглядит неплохо!

    Как следствие, в MLDv2 можно вообще отказаться от сообщений типа "итог" и сигнализировать о выходе из группы на языке множеств. Мы сознательно не пытаемся исключить недопустимые адреса источника, такие как групповые адреса, из первичного фильтра, поскольку наша работа ведется в предположении, что входящий пакет с недопустимым адресом источника будет отбракован на самой ранней стадии его обработки. Это позволяет упростить наши теоретико-множественные выкладки и сделать их более прозрачными.

    Но как нам быть со случаем, когда слушатель SFM на самом деле хочет заблокировать несколько источников, не препятствуя остальным? Этому отвечает первичное множество, равное дополнению множества блокируемых адресов . То есть разности множества всех адресов и множества блокируемых адресов. Нетрудно подсчитать, что при блокировании N адресов в первичном фильтре остается $$2^128-N$$ элементов, и при небольшом N это будет астрономическое число! Если множество всех адресов для режима ASM (N = 0) еще можно было бы кодировать кратко, как особый случай, режим SFM с блокированием потребует явного перечисления этого фантастического количества адресов, потому что мы заранее не знаем, какие источники предпочтет исключить слушатель. Очевидно, что ни передавать, ни хранить такое множество невозможно. Поэтому настало время нам перейти от красивой теории к практическим хитростям и трюкам.

    Главный наш трюк мы позаимствуем из сценария с блокированием источников. Пока степень наполнения фильтра адресами близка к одной из двух крайностей, все или ни одного, состояние фильтра можно записать довольно кратко. Когда наш первичный фильтр содержит всего несколько адресов, мы перечисляем именно их. Когда же первичный фильтр включает в себя почти все возможные адреса, то достаточно перечислить множество недостающих адресов $$\bar{S}$$, потому что оно связано с первичным множеством фильтра однозначным образом: $$\bar{S}=\S$$, где означает дополнение множества S до универсума (множества всех адресов IPv6).

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

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

    Со своей стороны, слушатель тоже фильтрует трафик группы в одном из этих двух режимов, потому что его касаются те же самые практические соображения насчет размера фильтра. И тогда поддержка SFM в MLDv2 сводится к следующему: во-первых, передать информацию о фильтрах от слушателей к маршрутизатору, а во-вторых, свести вместе информацию от разных слушателей, чтобы объединенный фильтр маршрутизатора удовлетворил их всех.

    В такой модели группового фильтра режим приема ASM (прием от любого источника) — это подмножество SFM и выражается фильтром EXCLUDE($$\varnothing$$), где ($$\varnothing$$ — пустое множество. Говоря по-русски, ASM — это прием от всех источников без исключения (подразумевая, что технически такое исключение возможно).

    Чтобы передавать такого рода информацию, каждая запись в отчете MLDv2 (см. рис 8.5(рис 8.5) Запись о группе в отчете MLDv2

    Чтобы провести начальную загрузку своего фильтра на маршрутизаторы, слушатель должен дословно перечислить адреса источника в фильтре и указать его режим работы. Для этой цели достаточно двух типов записи, CHANGE_TO_INCLUDE_MODE (тип 3) и CHANGE_TO_EXCLUDE_MODE (тип 4). Первый из них говорит, что слушатель перевел свой фильтр во включающий режим и загрузил в него приведенный список источников. Второй тип сообщает то же самое для фильтра в исключающем режиме. Чтобы нам было удобнее обсуждать теоретико-множественные свойства типов записей, мы станем их кратко записывать как TO_IN(S) и TO_EX(S) , соответственно, где S — множество источников, перечисленное в данной записи.

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

    В первом из них слушатель хочет разрешить или блокировать несколько дополнительных источников, не меняя остальной их список. Так возникают типы записи ALLOW_NEW_SOURCES (тип 5) и BLOCK_OLD_SOURCES (тип 6), сокращенно ALLOW(S) и BLOCK(S).

    Второй заслуживающий оптимизации случай — это периодический ответ на запрос маршрутизатора, которым слушатель сообщает свой текущий фильтр, хотя тот остается без изменений. Для этого он использует отдельные типы записи, MODE_IS_INCLUDE (тип 1) и MODE_IS_EXCLUDE (тип 2), сокращенно IS_IN(S) и IS_EX(S) . Благодаря этому маршрутизаторы могут оптимизировать обработку фильтров, пока те остаются постоянными. Кроме того, маршрутизатор не должен реагировать на эти записи дополнительными запросами, чтобы не вызвать зацикливание протокола.

    Чтобы не повторять все время "запись отчета" и не путать ее с записью о группе в оперативной памяти узла, впредь мы будем говорить просто "отчет", например, "отчет IS_EX", памятуя при этом, что соответствующее сообщение MLDv2 составное и может содержать в себе более одного отчета, каждый из них об определенной группе.

    Слушатель вполне может комбинировать разные отчеты об одной и той же группе, чтобы гибко уведомлять маршрутизаторы об изменениях в состоянии своего фильтра данной группы, используя при этом минимум сообщений MLDv2. К примеру, когда фильтр группы Г переходит из состояния INCLUDE( ) в состояние INCLUDE( ), где $$A$$ и $$B$$ — вообще говоря, разные множества источников, допущенных к данной группе Г, можно выделить два подмножества источников, затронутых этой операцией. Множество $$A$$ за вычетом множества $$B$$, в математической нотации $$A\B$$ больше не интересно слушателю, и его можно заблокировать. Напротив, множество $$B$$, за вычетом множества $$A$$ , то есть $$B\A$$, раньше не интересовало слушателя, так что его надо разблокировать и допустить к группе. В то же время, пересечение множеств $$A$$ и $$B$$, то есть $$A\cap B$$, остается интересным слушателю, и его статус не изменяется. Поэтому, чтобы объявить о данном переходе, достаточно два отчета в любом порядке: BLOCK( ) и ALLOW( ). Эти отчеты можно объединить в одно сообщение MLDv2, если позволяет их длина.

    Пусть читатель наглядно изобразит этот пример с помощью кругов Эйлера.

    В принципе, то же самое изменение можно было бы выразить всего одним отчетом TO_IN(B), однако этот отчет подразумевает, что изменился режим работы фильтра. В ответ маршрутизаторы начнут дополнительные проверки. Кроме того, если фильтр сам по себе длинный, а изменился он всего на пару источников, то суммарная длина двух отчетов ALLOW и BLOCK будет меньше, чем длина одного TO_IN, так как ALLOW и BLOCK передают только изменения, а TO_IN и TO_EX — фильтр целиком. Поэтому слушателю будет лучше воздержаться от применения TO_IN или TO_EX, когда подстраивается уже существующий фильтр.

    Используя наши соображения, составим сводку основных правил для слушателя группы в Табл. 8.2. Здесь $$A\backslash B$$ обозначает разность множеств и (множество всех элементов , которые не принадлежат также ), а ($$\varnothing$$ — пустое множество.

    Правила отчетности для слушателя MLDv2
    Текущее состояние Новое состояние Отчет
    начало приема или EXCLUDE( $$A$$) INCLUDE($$B$$ ) TO_IN( $$B$$)
    начало приема или INCLUDE($$A$$ ) EXCLUDE($$B$$ ) TO_EX($$B$$ )
    INCLUDE($$A$$ ) INCLUDE($$B$$ ) BLOCK($$A\backslash B$$), ALLOW($$B\backslash A$$)
    EXCLUDE($$A$$ ) EXCLUDE($$B$$ ) ALLOW($$A\backslash B$$), BLOCK($$B\backslash A$$)
    INCLUDE($$A$$ ) или EXCLUDE($$A$$ ) конец приема TO_IN($$\varnothing$$)

    На практике дополнительная сложность возникает из-за того, что источник должен повторить свой отчет несколько раз. Если группа изменит свое состояние во время повтора отчета о ней, надо объединить старый и новый отчет, используя все те же соображения теории множеств [§6.1 RFC 3810].

    Очевидная особенность SSM состоит в том, что о групповых адресах FF3x::/96 не должно быть отчетов IS_EX или TO_EX. Дело в том, что слушатель SSM заведомо заинтересован в ограниченном числе источников, и поэтому он может вести прием группы только в режиме INCLUDE [§2.2.2 RFC 4604].

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

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

    $$R=\bigcup_{i}I_i$$ .

    Если же все источники работают в исключающем режиме, то лучшее, что может сделать маршрутизатор, это блокировать пересечение всех исключающих фильтров :

    $$\bar R=\bigcap_{i}E_i$$.

    Ведь для множеств справедливо такое следствие законов де Моргана:

    $$(A\backslash B) \cup (A\backslash C)=A\backslash (B\cap C)$$.

    Если A — это множество всех адресов IPv6, а B и C — множества двух исключающих фильтров, то смысл этого равенства именно в том, что пересечение исключающих фильтров эквивалентно объединению включающих фильтров.

    А как быть, когда на канале есть слушатели группы в разных режимах? Снова применим правило объединения первичных множеств для создания удовлетворительного фильтра. При объединении множеств число элементов не уменьшается, а первичное множество любого исключающего фильтра, пока он разумной длины, уже содержит почти все адреса IPv6. Значит, и суммарный фильтр в своем первичном виде будет содержать почти $$2^{128}$$ элементов. Как мы знаем, в этом случае надо просто блокировать остальные адреса, чтобы не иметь дела с фильтром чудовищной длины. Поэтому подходящим режимом суммарного фильтра будет именно исключающий. Иными словами, как только хотя бы один из слушателей переходит в исключающий режим, то же самое вынуждены сделать и маршрутизаторы. При этом блокировать надо пересечение всех исключающих фильтров за вычетом объединения всех включающих фильтров:

    $$\bar R=\bigcap_{i}E_i \backslash \bigcup_{i}I_i $$.

    В частности, пока есть слушатели в режиме ASM$$\exists n: E_n=\varnothing$$, этот фильтр будет пустой и маршрутизатор не сможет блокировать ни один источник. Когда же последний слушатель в исключающем режиме прекратит прием группы, маршрутизатору следует вернуться во включающий режим, чтобы эффективнее фильтровать трафик группы. Если затем и слушатели во включающем режиме завершат прием, включающий фильтр опустеет: $$R=\varnothing $$, — что отвечает концу продвижения группы в данный канал.

    Это и есть теоретико-множественная основа поддержки SFM и SSM в MLDv2. Все остальное в ней — суть важные, но не несущие фундаментального значения детали. Среди них выделяется вопрос о том, как маршрутизатор станет управлять текущей информацией о группе, чтобы она оставалась актуальной. В MLDv1 для этого было достаточно связать с группой один таймер, а когда кто-то из слушателей прекращал прием группы, запросом о данной группе проверить, остались ли в ней другие слушатели. То есть элементарным объектом управления выступала группа. Теперь же разные слушатели изъявляют желание, или нежелание, принимать трафик группы из разных источников, но маршрутизатор по-прежнему не ведет поименного списка слушателей и не запоминает, кто именно из них заинтересован в данном источнике. Поэтому отдельный таймер потребуется каждой записи об источнике группы в памяти маршрутизатора [§7.2 RFC 3810], а элементарным объектом управления в MLDv2 станет источник данной группы, как это показано на рис 8.6(рис 8.6) Структуры данных группового маршрутизатора - модель MLDv2

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

    Когда слушатель группы заявляет отчетом IS_IN(A), TO_IN(A) или ALLOW(A) о своем желании принимать пакеты из некоторого множества источников A, маршрутизатор обязан немедленно допустить трафик из источников A в канал. Напротив, когда слушатель группы не желает вести прием из данного множества источников B и включает в свой отчет элемент IS_EX(B), TO_EX(B) или BLOCK(B) , маршрутизатор не вправе тут же заблокировать все множество источников B, потому что, возможно, у некоторых источников в нем есть и другие слушатели. Точно так же в MLDv1 маршрутизатор не мог прекратить продвижение трафика группы, едва получив итоговое сообщение от одного слушателя, — он был обязан проверить, остались ли у группы другие слушатели. Это вызвано тем, что маршрутизатор не ведет поименного учета слушателей.

    Тем не менее, по приходу отчета IS_EX(B), TO_EX(B) или BLOCK(B) маршрутизатору не обязательно проверять все множество блокируемых источников B. Ведь в этот момент у маршрутизатора уже есть определенное первичное множество допущенных источников S, закодированное в терминах INCLUDE или EXCLUDE . (Как мы говорили, во включающем режиме (INCLUDE ) первичное множество просто равно фильтру, который маршрутизатор хранит в своей памяти: $$S=R$$ , — а в исключающем режиме (EXCLUDE ) оно равно дополнению этого фильтра до множества всех адресов IPv6, нашего универсума: $$S=\bar R$$.) Поэтому проверить запросом необходимо только пересечение множеств B и S: $$Q=B\cap S$$, — так как адреса вне этого подмножества в любом случае сохраняют свое состояние: подмножество $$S\backslash B$$ по-прежнему остается допущенным к группе, а $$B\backslash S$$ уже заблокировано.

    Чтобы провести проверку источников направленно, маршрутизатору понадобится уточнить запрос MLD не только группой, о которой идет речь, но и множеством источников Q, состояние которых его интересует. Поэтому в MLDv2 у запроса появляется новая разновидность: запрос, ограниченный группой и источниками ( Multicast Address and Source Specific Query ). Нужно ли уточнять в таком запросе режим фильтрации, INCLUDE или EXCLUDE ? На самом деле, групповой маршрутизатор всегда спрашивает только о желательных источниках — он никогда не ставит вопрос: "Кто блокирует данный источник?" — потому что блокировка происходит по умолчанию. Это следует из самого предназначения MLD: избавиться от как можно большей доли ненужного группового трафика. Множество же $$Q=B\cap S$$ содержит не больше элементов, чем множество B, приведенное в отчете. Поэтому множество Q тоже можно указать простым перечислением его элементов. Следовательно, запрос MLDv2 ставится только в режиме INCLUDE , и явное указание режима в нем излишне. Такой запрос о группе Г, ограниченный списком источников Q, мы будем обозначать Q(Г,Q), а запрос, ограниченный только группой Г — просто Q(Г). Запрос Q(Г) отличается от Q(Г,Q) тем, что содержит ноль источников, то есть список Q в нем пуст. Их общий формат показан на рис. 8.7 и содержит несколько полей, которые понадобятся нам чуть позднее.

    (рис 8.7) Запрос MLDv2

    Хотя ограниченность запроса Q(Г,Q) режимом INCLUDE вполне обоснована, она приходит в противоречие с исключающим режимом работы фильтра и вызывает дополнительную сложность в управлении проверкой источников. Пока фильтр самого маршрутизатора работает во включающем режиме, отслеживать состояние проверки множества источников Q просто: Q — это заведомо подмножество фильтра $$(B\cap S\subseteq S)$$, о каждом элементе Q уже есть запись в памяти маршрутизатора, и потому при проверке достаточно понизить тайм-ауты на всех записях Q. Напротив, в исключающем режиме элементы Q заведомо отсутствуют в фильтре$$(B\cap S\subseteq \nsubseteq \backslash S)$$ , и маршрутизатору придется хранить записи об элементах Q отдельно от рабочего фильтра.

    Поэтому во включающем режиме состояние группы, с точки зрения маршрутизатора, можно задать всего одним списком источников, тогда как в исключающем режиме понадобятся целых два списка. Общепринятые обозначения этих состояний [§2.3 RFC 3810] приведены и прокомментированы в Табл. 8.3.

    Режимы фильтра группы в маршрутизаторе MLDv2
    Обозначение Режим Смысл параметров
    INCLUDE($$A$$) Включающий $$A$$ — "список включений" (Include List). Это список источников, допущенных к группе. Все прочие источники заблокированы.
    EXCLUDE($$X$$,$$Y$$) Исключающий X — "список требований" (Requested List). Это список источников, которые все еще могут быть интересны некоторым слушателям. Поэтому их нельзя немедленно блокировать, а необходимо проверить с помощью запроса MLD. Ниже мы встретим еще одну роль списка требований. Y — "список исключений" (Exclude List). Это список заблокированных источников. Все прочие источники, включая элементы X, допущены к группе.

    В состоянии EXCLUDE(X,Y) пересечение множеств X и Y, очевидно, пусто: $$X\cap Y= \varnothing$$, — поскольку нельзя одновременно допускать и блокировать один и тот же источник. Следовательно, X вообще не влияет на работу фильтра в текущий момент. Но если на записи об элементе $$x \in X$$ истечет тайм-аут, в модели MLD это будет означать, что данный источник x больше не интересен никому. Тогда маршрутизатор сможет перенести элемент x из списка X в список Y, тем самым заблокировав источник x и избавив канал от ненужного группового трафика. В этом и состоит роль списка X.

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

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

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

    Конечно же, мы помним, что запись об источнике привязана к определенной группе. Например, один и тот же источник И может быть допущен маршрутизатором к группе Г1, но заблокирован для группы Г2. В терминах заголовка IPv6 это означает, что пакеты от И к Г1 будут продвигаться в данный канал, а пакеты от И к Г2 — нет. Это находит прямое отражение в иерархии структур, которые маршрутизатор хранит в своей памяти.

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

    С другой стороны, список исключений — атрибут исключающего режима фильтра, а в этом режиме маршрутизатор и близко не может подойти к своей заветной мечте, а именно заблокировать все источники, потому что ему дозволено блокировать только перечисленные N из них, а остальные $$2^{128}-N$$ беспрепятственно шлют пакеты группе. Это настоящий кошмар для маршрутизатора! Поэтому в его интересах перевести фильтр группы во включающий режим, как только исчезнут слушатели в исключающем режиме. Маршрутизатор не ведет их поименного списка, и ему остается снова положиться на таймер. Это так называемый "таймер фильтра" (Filter Timer) на записи о группе. Он активен только в исключающем режиме и перезапускается по приходу отчета IS_EX или TO_EX, поскольку такой отчет говорит, что на канале все еще есть слушатели группы в исключающем режиме. Но если интервал этого таймера истечет, у маршрутизатора будет полное право переключить фильтр во включающий режим за отсутствием слушателей группы в исключающем режиме.

    Однако, как маршрутизатор ни стремится свести трафик группы на нет, он обязан допускать источники к группе по требованию ее слушателей. В частности, ему не пристало самовольно блокировать те источники, о которых недавно были отчеты IS_IN, TO_IN или ALLOW. Поэтому, переводя фильтр группы во включающий режим, маршрутизатор обязан позаботиться, чтобы новый список включений заранее содержал в себе, по меньшей мере, все записи о желательных источниках; ведь иначе они окажутся заблокированы по умолчанию. Выходит, что накапливать эту информацию фильтр должен заранее, еще в исключающем режиме. Понадобится ли для этого новый список источников в памяти маршрутизатора? Попробуем обойтись уже доступным списком требований, если он сможет играть обе роли. В принципе, маршрутизатору ничто не мешает сохранять записи о желательных источниках в этом же списке. Вопрос только в том, насколько оправдано хранить оба вида информации в одном списке: записи о желательных источниках и записи о тех источниках, которые маршрутизатор пытается заблокировать. Чтобы предотвратить путаницу, заметим такое простое свойство "собираемого" нами механизма: если пришел отчет IS_EX или TO_EX, то фильтр останется в исключающем режиме как минимум на протяжении интервала своего таймера. Если этот интервал будет, скажем, равен тайм-ауту записи об источнике, то у слушателей во включающем режиме будет еще достаточно времени, чтобы снова заявить о своем выборе источников в ответ на периодические запросы маршрутизатора. Поэтому маршрутизатор, получив отчет IS_EX или TO_EX, вправе вычистить из списка требований все записи, кроме текущих кандидатов на блокировку [§7.2.3 RFC 3810].

    Выше мы позволили себе вольность говорить о работе таймеров маршрутизатора в обыденных терминах "давно" и "недавно". В общем, понятно, что "недавно" — это пока интервал таймера не истек, а какой таймер, следует из контекста. Тем не менее, нам явно следует добавить определенности в эту картину. Мы сошлись на том, что у каждой группы есть два вида таймеров.

    Первый из них — это таймер одного источника данной группы. Во включающем режиме MLDv2 он играет ту же роль, что и таймер всей группы в MLDv1, а именно позволяет маршрутизатору определить, что пора блокировать трафик, так как его больше никто не принимает. Разница только в том, что маршрутизатор MLDv1 оперировал целыми группами, тогда как его "потомок" в MLDv2 работает тоньше и управляет отдельными источниками, вещающими группе. Поэтому, когда срабатывает такой таймер, в MLDv1 маршрутизатор удалил бы запись о группе, а в MLDv2 он удаляет вместо этого запись об одном источнике. Когда фильтр INCLUDE(A) опустеет, будет пора удалить и всю запись о группе, так что отельный таймер группы здесь не требуется.

    Насколько большой интервал понадобится таймеру источника? Этот таймер перезапускается по приходу отчета, что данный источник интересен какому-то слушателю данной группы, и поэтому интервал должен быть таким, чтобы заведомо перекрыть период между отчетами; ведь иначе состояние фильтра группы будет неустойчивым, фильтр станет "хлопать". Пока слушатели не меняют своего состояния, их отчеты вызывает маршрутизатор, высылая периодические запросы. Это довольно дорогая операция, так как, в теории, все слушатели канала должны в ответ сообщить свои фильтры в отчетах IS_IN или IS_EX, поэтому период между запросами, QI (Query Interval), должен быть довольно большим, по умолчанию 125 секунд [§9.2 RFC 3810].

    Вдобавок одиночный запрос мог не дойти до некоторых слушателей, скажем, из-за сбоя или перегрузки канала. Тем не менее, есть надежда, что какой-то из последующих запросов все-таки дойдет, и поэтому маршрутизатор должен подождать, как минимум, еще несколько интервалов QI, прежде чем объявить источник невостребованным и удалить его запись. Сколько всего раз надо передать сообщение, чтобы оно наверняка дошло, — это параметр RV (Robustness Variable), зависящий от ненадежности канала, по умолчанию 2 [§9.2 RFC 3810]. Такой повтор защитит от $$RV-1$$ потерь. Наконец, слушатели не ответят на запрос все хором, а сделают случайные паузы, чтобы распределить нагрузку на канал. Верхняя граница такой паузы — параметр QRI (Query Response Interval), по умолчанию 10 секунд [§9.3 RFC 3810].

    В результате, пока сеть стабильна, интервал таймера источника должен быть не менее чем $$QI \times RV+QRI$$; по умолчанию выходит 260 секунд. Этот интервал обозначают MALI (Multicast Address Listening Interval) [§9.4 RFC 3810].

    Интервал QI входит в MALI ровно RV раз, а не RV - 1, и вот почему. Нам никто не гарантирует, что возникновение записи об источнике совпадает с отправкой периодического запроса. Поэтому до ближайшего запроса придется подождать, самое большее, время QI. Затем следует RV - 1 интервалов длиной QI, когда запрос повторяется. Поэтому всего выходит RV интервалов длины QI.

    Чем пренебрегает формула для MALI? (Задержкой канала.)

    Но вот, представим себе, маршрутизатор получает отчет, который дает ему повод усомниться в некоторых записях, к примеру, отчет BLOCK. В ответ маршрутизатор начинает активную проверку этих записей запросами, ограниченными группой и списком источников. Эквивалентом этого сценария в MLDv1 был приход сообщения "итог". Чтобы справиться с проверкой поскорее, не жертвуя при этом достоверностью результатов, маршрутизатор передает запрос LLQC раз (Last Listener Query Count, по умолчанию равен RV [§9.9 RFC 3810]), но делает между передачами лишь короткие паузы длиной LLQI (Last Listener Query Interval), по умолчанию 1 секунда [§9.8 RFC 3810]. При этом слушатели задерживают свои отчеты на короткое случайное время от 0 до LLQI. В такой схеме самое позднее, когда может прийти отчет, это спустя время $$LLQI\times LLQC$$ от начала проверки, что по умолчанию составляет 3 секунды. Для этого интервала принято обозначение LLQT (Last Listener Query Time) [§9.10 RFC 3810]. И нет смысла устанавливать таймер источника во время проверки на больший интервал.

    Нам трудно избавиться от навязчивой мысли, что аббревиатуру LLQT следует произносить так: "Lil’ cutie".

    Интервал LLQI входит в LLQT ровно LLQC раз, а не LLQC + 1, потому что первый запрос в серии уходит немедленно. Затем следует LLQC - 1 интервалов повтора и, наконец, еще один интервал отведен на случайную задержку отчетов слушателями.

    Выходит, что в зависимости от ситуации оптимальный интервал таймера может быть длинным (

  • По умолчанию маршрутизатор устанавливает таймер источника на длинный интервал (MALI).
  • Когда маршрутизатор передает первое сообщение в серии запросов Q(Г,S), ограниченных данной группой Г и списком источников S, он понижает значение таймера на каждом источнике группы Г из списка S до LLQT, если оно выше LLQT.
  • Чуть позднее нам придется видоизменить эту процедуру, чтобы обеспечить более надежную синхронизацию между генератором запросов и пассивными наблюдателями. В некоторых случаях маршрутизатору придется устанавливать значение таймера на интервал LLQT при передаче не только первого, но и нескольких последующих запросов в серии.

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

    Прекрасно, теперь маршрутизатор может гибко управлять таймерами источников, пока фильтр группы работает во включающем режиме. А как быть с исключающим режимом? Будут ли его правила игры совершенно иными? Для начала напомним себе, что в этом режиме на месте единственного списка включений, содержавшего в себе все записи об источниках, возникает целых два списка, требований и исключений, а запись об источнике находится в одном из них. Функция списка требований такова, что и управлять им надо практически так же, как списком включений. Единственное существенное отличие заключается в том, что по тайм-ауту его записи не удаляются, а перемещаются в список исключений, потому что это кандидаты на блокировку. Оказавшись в списке исключений, запись об источнике вообще становится "бессмертной", а ее таймер не работает.

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

    Пока конфигурация слушателей постоянна, это должен быть длинный интервал (MALI), так как подготовка к переходу EXCLUDE$$\to$$INCLUDE включает в себя накопление записей в списке требований, а для этого каждый слушатель во включающем режиме должен получить возможность высказаться отчетом IS_IN в ответ на последний из периодических запросов маршрутизатора.

    Но возможна и ситуация, когда маршрутизатор получает повод немедленно усомниться в целесообразности исключающего режима. Таким поводом служит отчет TO_IN, косвенно говорящий об уменьшении числа слушателей группы в исключающем режиме. Получив его, маршрутизатор должен каким-то образом проверить, не упало ли оно до нуля. С этой целью маршрутизатор шлет серию из запросов Q(Г), ограниченных только группой, но не списком источников, пытаясь вызвать отклик слушателей группы в исключающем режиме за время LLQT. Наряду с ними ответят и все остальные слушатели группы, так что время подготовки к вероятному переходу EXCLUDE$$\to$$INCLUDE, если он произойдет ввиду отсутствия ожидаемого отклика, можно смело сократить до LLQT.

    По итогам наших рассуждений составим процедуру управления интервалом таймера фильтра — она оказывается практически такой же, как у таймера источника:

  • По умолчанию маршрутизатор устанавливает таймер фильтра на длинный интервал (MALI).
  • Когда маршрутизатор передает первое сообщение в серии запросов Q(Г), ограниченных только данной группой Г, он понижает значение таймера фильтра группы Г до LLQT, если оно выше LLQT.
  • Позднее нам придется видоизменить и эту процедуру, чтобы обеспечить надежную синхронизацию между генератором запросов и пассивными наблюдателями. В некоторых случаях маршрутизатору придется устанавливать значение таймера фильтра на интервал LLQT при передаче не только первого, но и нескольких последующих запросов в серии.

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

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

    Таким образом, у каждой разновидности запроса MLDv2 своя отдельная роль. Все они собраны в Табл. 8.4.

    Разновидности запросов MLDv2
    Разновидность Обозначение Когда шлется Роль
    Общий запрос Q(::) При старте маршрутизатора, затем периодически. Выяснить текущее состояние дел на канале.
    Запрос, ограниченный группой Г Q(Г) Когда уменьшается число слушателей группы в исключающем режиме. Выяснить, остались ли у данной группы слушатели в исключающем режиме.
    Запрос, ограниченный группой и списком источников Q(Г, S) Когда уменьшается число слушателей группы, заинтересованных в данных источниках S. Выяснить, востребованы ли все еще в данной группе данные источники.

    И вот, наконец, мы готовы составить свод правил для маршрутизатора MLDv2 в Табл. 8.5.

    Правила обработки отчетов маршрутизатором MLDv2
    Текущее состояние Отчет Новое состояние Запросы Удалить Таймеры
    INCLUDE($$A$$ ) ALLOW( $$B$$) INCLUDE$$(A\cup B)$$ - - ($$B$$ )=MALI
    INCLUDE($$A$$ ) IS_IN($$B$$ ) INCLUDE$$(A\cup B)$$ - - ( $$B$$)=MALI
    INCLUDE($$A$$ ) TO_IN( $$B$$) INCLUDE$$(A\cup B)$$ $$Q(Г,A\backslash B)$$ - ( )=MALI
    Комментарий: Текущий фильтр пропускает только источники из множества . Поэтому все три отчета, по сути, требуют расширения этого множества до $$A\cup B$$. На записях, о которых получена свежая информация, перезапускается таймер. Отчет TO_IN(B) также вызывает запрос насчет прочих допущенных источников, $$Q(Г,A\backslash B)$$, потому что это может быть сигнал о сокращении или изменении, а не расширении множества принимаемых источников. Отчет IS_IN(B) такого запроса не вызывает, потому что он сам пришел в ответ на какой-то запрос, во избежание зацикливания протокола.
    INCLUDE($$A$$) BLOCK($$B$$) INCLUDE($$A$$) $$Q(Г,A\cap B)$$ - -
    Комментарий: Поскольку текущий фильтр пропускает только источники из множества $$A$$ , подмножество уже заблокировано, и остается рассмотреть только $$A\cap B$$. Маршрутизатор не может немедленно заблокировать его, так как его потенциально принимают другие слушатели, поэтому сейчас он ограничивается запросом $$Q(Г,A\cap B)$$.
    INCLUDE($$A$$) IS_EX($$B$$) EXCLUDE($$A\cap B, B\backslash A)$$ - $$ A\backslash B $$ $$( B\backslash A)=0$$, (фильтр)=MALI
    INCLUDE($$A$$) TO_EX($$B$$) EXCLUDE($$A\cap B, B\backslash A$$) $$Q(Г,A\cap B)$$ $$ A\backslash B $$ $$( B\backslash A )=0 $$,(фильтр)=MALI
    Комментарий: Прежде всего, маршрутизатор должен перевести фильтр в исключающий режим, так как обнаружился слушатель в этом режиме. Все источники, кроме множества A , уже были заблокированы, поэтому сейчас можно смело блокировать такую часть B : $$B\cap (\backslash A)=B(\backslash A$$. Таймеры этих записей останавливаются, так как у списка исключений один таймер на весь фильтр; он как раз запускается. Остаток B , то есть $$A\cap B$$, немедленно блокировать нельзя, но его можно подвергнуть проверке, сохранив в списке требований. Тем не менее, запрос об источниках $$A\cap B$$ можно послать, только если отчет — об изменении фильтра (TO_EX), а не о его текущем состоянии (IS_EX), потому что последний сам пришел в ответ на какой-то запрос. Источники $$A \backslash B $$ в новом состоянии допущены по умолчанию, и записи о них можно вообще удалить.
    EXCLUDE($$X,Y$$ ) ALLOW($$A$$) EXCLUDE($$X\cup A, Y\backslash A$$) - - ($$A$$)=MALI
    EXCLUDE($$X,Y$$) IS_IN($$A$$) EXCLUDE($$X\cup A, Y\backslash A$$) - - ($$A$$)=MALI
    EXCLUDE($$X,Y$$) TO_IN($$A$$) EXCLUDE($$X\cup A, Y\backslash A$$) Q(Г,$$X\backslash A$$ ), Q(Г) - ($$A$$)=MALI

    Комментарий: Здесь мы знакомимся со второй ролью списка требований X, когда он накапливает включающие записи на случай перехода фильтра во включающий режим. Поэтому недостающие записи добавляются в список требований путем объединения множеств X и A. На всех записях, о которых получена свежая информация, перезапускается таймер. В то же время элементы A удаляются из списка исключений, который сокращается до $$ Y\backslash A$$. Ведь отчет требует прекратить блокировку A, и это должно произойти немедленно, так как погрешность фильтра допустима только в сторону разрешения источников.

    Отчет IS_IN запросов не вызывает, так как сам вызван запросом. Напротив, отчет TO_IN вызывает запрос насчет тех элементов X, о которых нет свежей информации: X за исключением A. По большому счету, он нужен, чтобы список X не рос за счет устаревших записей. Кроме того, отчет TO_IN потенциально означает, что число слушателей в исключающем режиме уменьшилось, потому что один из них перешел во включающий режим. Чтобы проверить, остались ли они вообще, маршрутизатор шлет еще один запрос без уточнения источников, то есть ограниченный только группой: Q(Г). Хотя в этом случае запрос $$Q(Г, X\backslash A)$$ может показаться избыточным, он нужен для координации таймеров между маршрутизаторами канала — о ней мы скоро поговорим.

    EXCLUDE($$X,Y$$) IS_EX($$A$$) EXCLUDE($$ A\backslash Y, Y\cap A$$) - ($$ X\backslash A, Y\backslash A $$) ($$ A\backslash X\backslash Y $$)=MALI,(фильтр)=MALI
    EXCLUDE($$X,Y$$) TO_EX($$A$$) EXCLUDE($$ A\backslash Y, Y\cap A$$) Q(Г,$$ A\backslashY $$ ) $$ X\backslash A, Y\backslash A $$) ($$ A\backslash X\backslash Y $$)=MALI, (фильтр)=MALI

    Комментарий: Безопасно блокировать можно только пересечение $$ Y\cap A $$, так как только насчет этого подмножества есть консенсус слушателей. Остаток Y , то есть $$ Y\backslash A $$, нужно немедленно разблокировать, потому что он интересен данному слушателю. Остальную часть A , то есть $$ A\backslash Y $$, надо сначала проверить запросом, если мы имеем дело с отчетом об изменении состояния (TO_EX).

    При этом роль списка требований меняется: он снова хранит записи о проверяемых источниках, кандидатах на блокировку, а все прочие записи из него удаляются. Подмножество $$ X\backslash A $$ уходит из списка требований, потому что в новых условиях у него больше нет шансов на блокировку: слушатель хочет его принимать. В то же время, подмножество $$ X\cap A $$ остается в списке требований, потому что это по-прежнему кандидат на блокировку.

    На вновь добавленных записях, которых раньше не было в списке требований (это $$ A\backslash X\backslash Y $$), запускаются таймеры. В случае TO_EX они устанавливаются на интервал, равный текущему значению таймера фильтра. Почему так? Пока фильтр работает в исключающем режиме, его таймер отсчитывает время до желанного момента, когда маршрутизатор сможет заблокировать почти все источники. Поэтому таймер фильтра неявно "тикает" на всех источниках, которые сейчас не занесены в или и потому разрешены по умолчанию. Ради сохранения информации об актуальности таких записей, мы должны скопировать значение таймера фильтра в таймер такого источника, когда мы создаем о нем явную запись в списке требований . Именно это здесь и происходит: сохранение накопленной информации.

    Случай IS_EX особенный. Если маршрутизатор получил сообщение IS_EX, то есть заявление об уже существующем состоянии слушателя, и подмножество $$ A\backslash X\backslash Y $$ не пусто, то это значит, что маршрутизатор потерял синхронизацию со слушателем. Хотя такое может произойти, например, когда маршрутизатор едва только включился в работу, или же после сбоя канала, наиболее вероятная причина — что маршрутизатор сам удалил эти записи из списка требований, чтобы проверить другие источники. Скажем, именно это произойдет, если на канале несколько слушателей в исключающем режиме, фильтры которых не совпадают: список требований станет "осциллировать". (Убедитесь в этом, изучив сценарий, когда один слушатель блокирует множество {1,2}, а другой — {2,3}.) Так или иначе, маршрутизатор не может доверять своим текущим сведениям и устанавливает таймеры новых записей на длинный интервал MALI. Кроме того, маршрутизатор не шлет запрос, ограниченный группой и списком источников, так как отчет IS_EX уже вызван каким-то запросом.

    У нас есть гипотеза, что установка ($$ A\backslash X\backslash Y $$)=(фильтр) и в этом случае не вызвала бы никаких проблем. Исследуйте ее, если будет время и желание.

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

    EXCLUDE($$X,Y$$) BLOCK($$A$$) EXCLUDE$$(X\cup ( A\backslash Y),Y)$$ ( Q(Г, $$A\backslash Y $$) - ($$ A\backslash X\backslash Y $$)=(фильтр)
    Комментарий: Это правило похоже на предыдущее, TO_EX. Часть множества A, возможно, уже заблокирована фильтром Y, поэтому изменения касаются только подмножества $$ A\backslash Y $$. Как обычно, вместо его непосредственной блокировки следует запрос о нем. Тем не менее, отчет BLOCK может исходить от слушателя в любом режиме, и поэтому маршрутизатор только добавляет недостающие элементы в список требований X путем объединения множеств, а не сбрасывает его до $$ A\backslash Y $$. По той же самой причине таймер фильтра продолжает отсчет времени, а не перезапускается; ведь отчет BLOCK не доказывает, что есть слушатели в исключающем режиме. Если его интервал вскоре истечет, фильтр перейдет во включающий режим, а в список включений попадут все желательные источники, потому что список требований не сбрасывался до проверяемого подмножества, а объединялся с ним. Так маршрутизатор избежит нежелательной блокировки источников при переключении фильтра. Об установке таймеров на источниках см. предыдущее обсуждение TO_EX. В отличие от правила TO_EX, здесь список исключений Y не требует немедленных изменений. Дело в том, что объявление вида TO_EX(A) говорит, что данному слушателю интересны все источники кроме перечисленных во множестве A, а значит, их надо немедленно разблокировать. В то же время, объявление BLOCK(A) не несет в себе такого строгого требования.

    В отличие от правила TO_EX, здесь список исключений не требует немедленных изменений. Дело в том, что объявление вида TO_EX(A) говорит, что данному слушателю интересны все источники кроме перечисленных во множестве A, а значит, их надо немедленно разблокировать. В то же время, объявление BLOCK(A) не несет в себе такого строгого требования.

    Заданием для читателя будет изобразить и проанализировать диаграммы Эйлера, отвечающие этим правилам.

    Также убедитесь, что отчеты идемпотентны, то есть несколько копий одного отчета подряд, приведут маршрутизатор в то же самое состояние, что и ровно один экземпляр отчета. Это свойство позволяет слушателям повторять свои отчеты не по запросу, чтобы они наверняка дошли.

    А обладают ли отчеты свойствами коммутативности и транзитивности? (Повод подумать.)

    Все отчеты кроме IS_EX и TO_EX обладают свойством аддитивности: если данный список источников разбить, скажем, на два подсписка и послать их в разных отчетах, то окончательное состояние маршрутизатора окажется тем же самым. Зачем это может пригодиться, догадаться несложно: если отчет о данной группе содержит так много источников, что сообщение превышает MTU канала, его можно разбить на несколько сообщений поменьше. Однако для IS_EX и TO_EX этот трюк не работает. Как тогда быть? Приемлемым решением будет передать только часть списка одним сообщением [§5.2.15 RFC 3810]. Ведь маршрутизатор MLD вправе ошибаться в разрешительную сторону, если это необходимо, а опустить часть источников в отчете IS_EX или TO_EX, значит, допустить их к группе.

    Подсчитайте, сколько источников может уместиться в одно сообщение MLDv2, когда MTU канала равно минимально возможному значению 1280 байт. (Подсказка: учесть опцию "сигнал маршрутизатору. Ответ: {1280[MTU]-40[IP]-8[HopByHop-RtrAlert]-28[MLDv2]} div 16 = 75.)

    Оказывается, что сложность правил MLDv2 (а также IGMPv3) обусловлена, главным образом, поддержкой слушателей в режиме EXCLUDE(A), где множество A не пусто. Рассмотрите упрощенный протокол LW MLDv2 (облегченный MLDv2), где слушатель может принимать или избранные источники (INCLUDE(A)), или все источники подряд (EXCLUDE($$ \varnothing$$)), но не может блокировать избранные источники. В этом случае правила маршрутизатора становятся намного проще, потому что ему больше не нужно вести список исключений. Самое удивительное, что маршрутизатор LW MLDv2 может быть совместим со слушателями MLDv2, потому что в MLD допустима погрешность в сторону разрешения источников. Маршрутизатору LW MLDv2 достаточно рассматривать любые отчеты IS_EX(A) или TO_EX(A) как просьбу разрешить все источники, то есть IS_EX($$ \varnothing$$) или TO_EX($$ \varnothing$$), соответственно. Если вы не справитесь с этим заданием самостоятельно, обратитесь к [RFC 5790].

    Такое выражение протокола MLDv2 на языке теории множеств имеет одну любопытную особенность. Когда составленная нами таблица предписывает послать запрос, ограниченный группой и списком источников, может так оказаться, что список источников пуст. Например, в последнем правиле таблицы, EXCLUDE/BLOCK, такая ситуация возникнет, если A — это подмножество Y: $$A\subseteq Y\Rightarrow A\backslash Y= \varnothing$$. На первый взгляд, выходит, что надо послать запрос Q(Г, $$ \varnothing$$), где $$ \varnothing$$ — пустое множество. Но, если мы составим такой запрос, то он окажется неотличим от запроса, ограниченного только группой, Q(Г) — см. формат запроса MLDv2. Как разрешить эту двусмысленность? На самом деле, очень просто: запрос Q(Г, $$ \varnothing$$) риторический и никакого ответа не предполагает, а значит, и слать его совершенно ненужно. Ведь запрос всегда ставится в режиме INCLUDE, и на пустой список источников ответа никогда не было бы в принципе. Иными словами, единственный режим слушателя, который мог бы вызвать ответ на Q(Г, $$ \varnothing$$), — это INCLUDE($$ \varnothing$$), но этот режим эквивалентен прекращению приема группы.

    Выражаясь на модном сегодня новоязе а-ля "1984", это не-режим не-слушателя.

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

    В связи с этой процедурой возникает таймер, с помощью которого наблюдатель может обнаружить, что текущий генератор запросов ушел со сцены. Этот таймер перезапускается на интервал OQPT (Other Querier Present Timeout, тайм-аут присутствия другого генератора запросов) всякий раз, когда приходит запрос MLD. Если же происходит тайм-аут, значит, запросов давно никто не слал, и пора начать новые выборы. Стандарт предлагает величину OQPT, равную $$QI\times RV+\frac{1}{2}QRI$$ [§9.5 RFC 3810]. Однако на наш взгляд, добавка $$\frac{1}{2}QRI$$ способна привести к тому, что новый генератор запросов возникнет слишком поздно и непрерывность маршрутизации группового трафика будет нарушена. Убедиться в этом просто. Рассмотрите канал с одним слушателем и двумя маршрутизаторами. Один из маршрутизаторов, очевидно, генератор, а второй наблюдатель. Когда генератор исчезает, наблюдатель ждет время OQPT, прежде чем послать серию запросов. Затем слушатель вправе задержать свой отчет на время вплоть до QRI. В результате отчет может запоздать, придя через время $$ OQPT+QRI-MALI=\frac{1}{2}QRI$$ после того, как оставшийся маршрутизатор прекратил продвижение группы в канал по тайм-ауту MALI. Более подходящим значением для OQPT будет просто $$QI\times RV$$.

    Когда маршрутизатор только включается в работу или участвует в выборах, ему надо позаботиться, чтобы его запрос наверняка услышали другие маршрутизаторы, а также слушатели. Ради устойчивости к потере пакетов он передает такой запрос несколько раз (по умолчанию RV [§9.7 RFC 3810]), разделяя повторы паузами (по умолчанию $$\frac{1}{4}QRI$$ [§9.6 RFC 3810]).

    После выборов все маршрутизаторы кроме генератора запросов замолкают и ведут только пассивное наблюдение за чужими сообщениями MLD. Чтобы такая система работала устойчиво, состояние наблюдателей должно быть согласовано с генератором в том, что касается управляющих записей и таймеров. Для этого, в первую очередь, наблюдатели должны получить от генератора его текущие значения параметров QI и RV, так как от них зависит длина интервала MALI. Эти значения обозначают, соответственно, QQI (Querier’s QI) и QRV (Querier’s RV), чтобы подчеркнуть, что они исходят от генератора запросов.

    Как следствие, формат запроса MLDv2 должен предусмотреть поля для этой информации. Значение QRV можно поместить в пакет как есть, в беззнаковом коде. Соответствующее поле тоже обозначают QRV (Querier’s Robustness Variable, RV генератора запросов) [§5.1.8 RFC 3810]. В то же время, значение QQI, если представить его как число миллисекунд в 16-битном беззнаковом коде, может расти только до 65,535 секунд, а кроме того, миллисекундная точность на всем диапазоне значений здесь излишня. Поэтому для передачи QQI мы чуть позже разработаем новый код. Он же пригодится и для передачи значений MRD (максимальной задержки отчета). Поле для хранения QQI в этом коде обозначают QQIC (Querier’s Query Interval Code, код интервала запросов генератора) [§5.1.9 RFC 3810].

    Далее, когда дело доходит до быстрой проверки группы, в игру вступают интервал LLQI, счетчик повторов LLQC и тайм-аут LLQT. Значения этих параметры тоже должны быть согласованными, чтобы наблюдатель не удалил запись об источнике или не переключил режим фильтра группы раньше времени. Впрочем, эти параметры явным образом передавать не надо. Вот наше тому обоснование:

  • По умолчанию значение LLQC совпадает с RV, а рекомендуемое значение RV уже объявляется генератором запросов в поле запроса QRV.
  • При быстрой проверке максимальная задержка отчета, MRD, равна LLQI, а значение параметра MRD уже содержится в запросе. Поэтому наблюдателям достаточно выполнить обратную операцию и принять рабочее значение LLQI равным MRD из полученного запроса.
  • Значение LLQT однозначным образом зависит от LLQI и $$LLQC$$: $$LLQI \times LLQC$$.
  • Чтобы не быть голословными, сошлемся на существующие реализации. Например, именно так поступает модуль MLDv2 в XORP http://www.xorp.org/ : он принимает LLQC равным QRV, а LLQI равным MRD, если принят запрос с ненулевым адресом группы.

    Но каким образом наблюдатели узнают, что началась быстрая проверка? Эту информацию можно извлечь из того, какого вида запрос:

  • Если запрос общий, с нулевым адресом группы и пустым списком источников, то это явно не быстрая проверка, а начальный или периодический запрос. Из такого запроса надо извлечь только значения RV и QI.
  • Если запрос ограничен только ненулевой группой, а список источников в нем пустой, то это быстрая проверка, остались ли на канале слушатели группы в исключающем режиме. В этом случае надо понизить интервал таймера на фильтре указанной группы до LLQT. Согласно принципу Постела, получатель запроса должен сначала убедиться, что фильтр указанной группы работает в исключающем режиме.
  • Если же запрос ограничен ненулевой группой и непустым списком источников, то он служит для быстрой проверки указанных источников данной группы. Для наблюдателя это повод понизить интервалы таймеров на всех перечисленных источниках данной группы до LLQT.
  • Конечно, наблюдателю не помешало бы убедиться, что запрос исходит именно от генератора запросов. Пунктуальная реализация может помнить адрес текущего генератора и обновлять его, если пришел запрос с еще меньшим адресом источника IPv6. Однако в этом случае придется также учесть сценарий с исчезновением генератора. На практике достаточно сравнить адрес источника запроса с собственным адресом наблюдателя и проигнорировать запрос, если наш локальный адрес меньше. Остальную работу по стабилизации системы здесь выполнит механизм выборов генератора. Ведь он гарантирует , что в конце концов останется ровно один активный генератор, который и станет диктовать остальным значения параметров.

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

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

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

    Какие сложности это вызывает? В нашей рабочей схеме каждый экземпляр запроса управляет таймерами наблюдателей, понижая их интервал до небольшой величины LLQT. Поэтому возможен, к примеру, такой сценарий с участием генератора, наблюдателя и слушателя, составленный для LLQC = 2:

  • Генератор шлет первый экземпляр запроса Q(Г,S).
  • Наблюдатель понижает таймеры на источниках S до LLQT.
  • Слушатель после короткой паузы отвечает отчетом, подтверждая свой интерес к источникам S.
  • Генератор и наблюдатель получают отчет и перезапускают таймеры S с длинным интервалом MALI.
  • Генератор повторяет запрос Q(Г,S).
  • Наблюдатель понижает таймеры на источниках S до LLQT.
  • Слушатель не отвечает или отчет не доходит до наблюдателя.
  • Наблюдатель получает тайм-аут на источниках S и блокирует их.
  • Синхронизация генератор-наблюдатель нарушена!
  • Аналогичный сценарий можно составить и для запроса Q(Г), ограниченного только группой. В нем у наблюдателя сработает таймер фильтра.

    К сожалению, генератор запросов действует в условиях недостатка информации и поэтому не может дать стопроцентную гарантию того, что наблюдатель получит все необходимые сведения даже после сбоя сети. Видимо, лучшее, что мог сделать генератор на шаге 5 нашего сценария, — это сообщить наблюдателю: больше не понижай таймеры, так как источники S все еще востребованы в данной группе. Эту информацию можно закодировать одним битом в формате запроса. По смыслу этот бит — просто флаг, надо ли наблюдателям понижать соответствующие таймеры. Он возникнет на месте поля "резерв", поэтому его значение 1 отвечает новому поведению: не понижать таймеры. Отсюда его название: "подавить обработку маршрутизаторами" (Suppress Router-Side Processing), сокращенно "флаг S" [§5.1.7 RFC 3810].

    Как генератор запросов определит, что пора установить этот флаг? Если мы предположим, что слушатели подтвердили свой интерес ко всем источникам множества S в запросе, то будет достаточно отметить этот факт установкой флага S в том образе запроса, который генератор хранит в своей оперативной памяти. Тогда последующие копии запроса уйдут с S = 1. Однако в действительности может оказаться, что подтвержден интерес только к подмножеству источников $$S_1\subset S$$. Как генератору запросов разделить подмножества $$S_1$$ и $$S_2=S\backslash S_1$$, когда придет время повторить запрос? Здесь ему помогут значения таймеров. Ведь у подтвержденного подмножества таймеры перезапущены и снова отсчитывают длинный интервал MALI, тогда как у неподтвержденного подмножества значения таймеров по-прежнему не превышают LLQT. Так как флаг S один на все сообщение, генератору придется разбить запрос на два. В первом из них будет перечислено подмножество источников $$S_1$$ и установлен флаг S (S = 1); во втором же окажется подмножество источников $$S_2$$, а флаг S будет сброшен (S = 0).

    Флаг S имеет смысл и для запросов, ограниченных только группой. В этом случае речь идет о подтверждении исключающего режима. Так как маршрутизатор не может находиться в нем наполовину, делить будущие копии запроса ему точно не придется. Тем не менее, критерий, установить ли флаг S, может быть тем же, с точностью до замены таймера источника на таймер фильтра: если таймер фильтра по-прежнему не превышает LLQT, то S = 0, а если он вдруг стал больше LLQT, то значит, пора установить S = 1.

    Теперь наблюдатели понижают таймеры не по любому запросу Q(Г) или Q(Г,S), а только если в нем S = 0. Для нас это повод уточнить условия, когда то же самое делает генератор запросов. Если бы наблюдателей не было вообще, генератор мог бы сделать это единожды, в самом начале серии запросов. Ведь вся наша арифметика тайм-аутов MALI и LLQT была основана именно на этой модели. С другой стороны, если бы наблюдатели существовали, но пакеты всегда достигали бы цели, генератор тоже мог бы понизить таймеры в начале серии, а флаг S установить сразу после первого запроса, чтобы наблюдатели поступили с таймерами так же. То есть первый запрос серии вызвал бы установку таймеров на всех маршрутизаторах канала, а последующие дубликаты запроса на них бы не влияли. Но в действительности первый запрос серии может потеряться, как любой другой. Именно поэтому генератор запросов устанавливает флаг S не раньше, чем он получит подтверждение, что энный запрос хоть до кого-то дошел (в данном случае до слушателя). Поэтому пусть ради синхронизации с наблюдателями генератор тоже продолжает понижать соответствующие таймеры до LLQT всякий раз, пока он передает запрос с S = 0.

    Конечно, этот трюк нарушает четкость нашего плана, как согласовать тайм-аут записи с ритмом повторяемых запросов, но он помогает синхронизации маршрутизаторов канала. С одной стороны, теперь время жизни записи может продлиться дольше, чем это необходимо. Так, если заинтересованных слушателей не осталось и на запрос ответить некому, запись проживет интервал LLQT после последнего запроса, хотя по нашему первоначальному плану она прекратила бы свое существование через минимально необходимое время LLQI. С другой стороны, теперь выше вероятность того, что тайм-аут записи произойдет практически одновременно на всех маршрутизаторах.

    Окончательные правила синхронизации между генератором запросов и пассивными слушателями оказываются такими:

  • Подготовка к передаче запроса — только для генератора [§7.6.3 RFC 3810]:

    a. Запрос, ограниченный только группой Г: Если таймер фильтра Г меньше или равен LLQT, то флаг S надо сбросить в 0, а иначе установить в 1.

    b. Запрос, ограниченный группой Г и списком источников S: Если все таймеры S меньше или равны LLQT, то флаг S надо сбросить в 0, а если больше — установить в 1. В смешанном случае запрос надо разбить на два, один с S = 0, а другой с S = 1.

  • Действия по приему или передаче запроса — как для генератора, так и наблюдателей [§7.6.1 RFC 3810]:

    a. Если в запросе флаг S сброшен в 0:

    i. Запрос, ограниченный только группой Г: Понизить таймер фильтра Г до LLQT.

    ii. Запрос, ограниченный группой Г и списком источников S: Понизить таймеры источников из списка S до LLQT.

    b. Если же в запросе флаг S установлен в 1, то текущие таймеры не трогать.

  • Эта процедура действительно способна помочь синхронизации между генератором запросов и наблюдателями после кратковременного сбоя в сети. Например, если в нашем вышеизложенном сценарии самый первый отчет слушателя дойдет до наблюдателя, но не дойдет до генератора, наблюдатель повысит таймеры до MALI, тогда как у генератора они продолжат отсчитывать короткий интервал LLQT. Однако затем генератор повторит свой запрос с S = 0, и наблюдатель снова понизит таймеры, так что синхронизация будет восстановлена.

    На этом мы почти завершили усовершенствование MLD и практически готовы объявить вторую версию протокола готовой к применению. Последнее, о чем нам следует подумать, — это совместимость с первой версией.

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

    Совместимость сообщений — это, по сути, однозначность их интерпретации. Скажем, если в запросе MLDv2 мы изменим код поля "максимальная задержка отклика" , В MLDv1 это поле обозначалось как MRD. то нам надо позаботиться, чтобы слушатель MLDv1 не применил к этому полю ошибочную интерпретацию. Продемонстрируем это на практике. Мы как раз собирались разработать новый код для значений QI и MRD в запросе, так что давайте сделаем его обратно совместимым с целым беззнаковым кодом.

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

    Наша цель — представить широкий диапазон значений с разумной точностью, а этому условию отвечает код с плавающей запятой, похожий на экспоненциальную (научную) нотацию чисел. В этом коде число приближенно записывают как мантиссу в оптимальном диапазоне, помноженную на степень основания системы счисления, в данном случае 2:$$x=M\cdot 2^E$$. Здесь M — мантисса фиксированной точности, а E — целочисленный порядок числа.

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

    Хотя обычно принимают, что в нормализованной записи запятая находится после самого старшего разряда мантиссы, нам сейчас удобнее поместить ее после самого младшего разряда, чтобы мантисса оставалась целой. Очевидно, что эти два представления однозначно преобразуются друг в друга простым сдвигом, то есть изменением порядка E на число явно хранимых разрядов мантиссы, если старший разряд подразумевается. Например, двоичная мантисса 1,0001 преобразуется в 10001 вычитанием 4 из порядка, чтобы значение закодированного числа оставалось неизменным.

    Применим такой код к 16-битному полю, где будет храниться значение MRD. Ввиду нового кода это поле и обозначают по-другому: MRC (Maximum Response Code, код максимальной задержки отклика) [§5.1.3 RFC 3810]. Пусть порядок занимает 3 бита. Еще один бит "съел" флаг кода. Тогда на мантиссу остается 12 бит. Значения до 32767 мы уже можем представить в старом коде, поэтому нам интересен диапазон от $$32768=2^{15}$$ и выше. Этому отвечают значения порядка от 3: $$\log_2 \frac{2^{15}}{2^{12}}=3$$. Поэтому нам следует сместить порядок на 3 для расширения диапазона. Двоичные значения поля мантиссы и поля порядка будут представлены в обычном целом беззнаковом коде. Тогда численное значение MRD в новом коде будет равно $$(0x1000+M^\prime)\cdot 2^{E^\prime+3}$$.

    Рассчитайте, каковы минимальное и максимальное значения поля MRC в новом коде. (32 768 и 8 387 584.)

    Тем же самым способом мы решим задачу о кодировании поля QQIC для хранения значений QQI [§5.1.9 RFC 3810]. Это поле длиной 8 бит, причем 3 из них отведены под порядок и 4 под мантиссу. Следовательно, численное значение поля, когда его старший бит установлен, следует вычислять так: $$QQI=(0x10+M^\prime)\cdot 2^{E^\prime+3}$$.

    Рассчитайте, каковы минимальное и максимальное значения поля QQIC в новом коде. (128 и 31 744.)

    Расширенные форматы полей MRC и QQIC в отчете MLDv2 показаны на рис. 8.8.

    (рис 8.8) Код с плавающей запятой для полей MRC и QQIC

    Решив вопрос о совместимости на уровне отдельных полей, перейдем к совместимости формата сообщений в целом. По своему формату отчет MLDv2 в корне отличается от отчета MLDv1, так как он содержит переменное число записей о разных группах, а каждая запись имеет определенный тип и может содержать список источников. Лучшее, что мы можем сделать ради совместимости как однозначности интерпретации, — это полностью разделить отчеты MLDv1 и MLDv2. Для этого достаточно назначить отчету MLDv2 новый тип ICMPv6 (143).

    Что же касается запросов, то их формат изменился не так радикально. Более того, он вполне отвечает модели расширения протокола MLD, когда новые данные помещаются в конец сообщения, после его совместимой части. Благодаря этому запрос MLDv2 может сохранить за собой тот же самый тип ICMPv6 (130). Если реализация поддерживает обе версии MLD, то ей просто определить версию запроса на входе: запрос MLDv1 всегда длиной 24 байта, тогда как длина запроса MLDv2 — от 28 байт и выше. Диапазон длин от 25 до 27 байт запретный и не отвечает никакой версии MLD.

    Обратите внимание, что длина сообщения MLDv2 — как запроса, так и отчета — закодирована в самом сообщении. Поэтому отличить будущие версии от MLDv2 можно будет, только декодировав совместимую с MLDv2 часть.

    Наконец, мы добрались до совместимого поведения сторон протокола. Здесь нас, в первую очередь, интересуют два случая: поведение слушателей MLDv1 в ответ на запросы MLDv2 и поведение маршрутизаторов MLDv2 в ответ на отчеты MLDv1. Это видно из таблиц совместимости Табл. 8.6 и Табл. 8.7.

    Совместимость слушателей MLD
    Версия слушателя Запрос MLDv1 Запрос MLDv2
    MLDv1 нет проблем как быть?
    MLDv2 отчет MLDv1 нет проблем
    Совместимость маршрутизаторов MLD
    Версия маршрутизатора Отчет или итог MLDv1 Отчет MLDv2
    MLDv1 нет проблем проигнорирует
    MLDv2 как быть? нет проблем

    Слушатели MLDv2 не должны отвечать отчетами MLDv2 на запросы MLDv1 хотя бы потому, что маршрутизатор MLDv1 проигнорирует их ввиду неизвестного типа ICMPv6.

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

    Это естественным порядком приводит нас ко второму случаю. Маршрутизатор MLDv2 как более современный легко декодирует сообщение MLDv1, поэтому вопрос состоит в том, насколько усложнит реализацию режим совместимости. В частности, понадобится ли маршрутизатору MLDv2 отдельный алгоритм реакции на сообщения MLDv1? К счастью, нет, потому что по содержащейся в них информации отчет и итог MLDv1 — это лишь подмножество многофункциональных отчетов MLDv2. На самом деле, мы об этом уже говорили, так что сейчас нам осталось только составить таблицу соответствия — Табл. 8.8.

    Апгрейд" сообщений MLDv1 до MLDv2
    Сообщение MLDv1 Эквивалент MLDv2
    Отчет IS_EX($$ \varnothing $$)
    Итог TO_IN($$ \varnothing $$)

    Благодаря такому соответствию, маршрутизатор MLDv2 может обрабатывать отчеты и итоги MLDv1 по уже готовым правилам протокола MLDv2.

    Конечно, маршрутизатору MLDv2 придется учесть, что адрес назначения отчета или итога MLDv1 равен группе, о которой идет речь, а не FF02::16. Это не вызовет проблем, так как маршрутизатор ведет теневой прием всех групп и руководствуется, в первую очередь, опцией "сигнал маршрутизатору" в сообщениях MLD, а не их адресами назначения.

    Помимо этих двух очевидных случаев взаимодействия сторон MLD, нам надо обратить внимание на "перекрестное опыление" слушателей и маршрутизаторов (Табл. 8.9). Ведь в принципе слушатели могут получать отчеты, а маршрутизаторы — запросы. К счастью, с первой частью проблем нет, так как отчеты MLDv2 направляются по специальному групповому адресу, и слушатели их не получат, независимо от их версии. Что касается реакции слушателя MLDv2 на отчет MLDv1, связанная с этим оптимизация (подавление отчетов) упразднена, и отчет можно безопасно игнорировать.

    Тем не менее, слушателю MLDv2 не запрещено подавлять свои отчеты при получении чужих отчетов MLDv1 [§8.2.2 RFC 3810].

    Перекрестное опыление" слушателей MLD
    Версия слушателя Отчет или итог MLDv1 Отчет MLDv2
    MLDv1 нет проблем не получит
    MLDv2 может игнорировать не получит

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

    Поэтому, ради устойчивой работы сети, мы объявим нежелательной смешанную конфигурацию с использованием маршрутизаторов MLDv1 и MLDv2 на одном канале. Точнее, пусть маршрутизатор MLDv2 использует только запросы MLDv1, пока на том же канале есть маршрутизаторы MLDv1 [§8.3.1 RFC 3810]. Это приемлемый компромисс, так как маршрутизаторов меньше, чем хостов, а их взаимозаменяемость выше. Если обновление хостов — процесс долгий , трудоемкий и требующий поэтапного подхода, переключение подсети В старом смысле этого термина: транзитная инфраструктура сети. на MLDv2 можно провести за один шаг, например, путем установки на канал одного или нескольких новых групповых маршрутизаторов и временного отключения старых. Затем будет достаточно постепенно обновлять старые маршрутизаторы и возвращать их обратно в эксплуатацию.

    Предложите решение для вот какой проблемы. Допустим, последний маршрутизатор MLDv1 только что убрали с канала. Но маршрутизаторы MLDv2, находясь в режиме совместимости, получают запросы MLDv1 друг от друга и потому пребывают в уверенности, что режим совместимости по-прежнему необходим. Могут ли маршрутизаторы MLDv2 самостоятельно выйти из режима совместимости, а если да, то как? (RFC предлагает ручную настройку.)

    Сводная таблица совместимости между сторонами MLDv1 и MLDv2 будет такой, как показано в Табл. 8.10.

    Совместимость между сторонами MLD: сообщения какой версии использовать?
    Хосты $$\downarrow$$ \ Маршрутизаторы $$\to$$ Только v1 Только v2 v1 и v2
    Только v1 v1 Запросы v2, отчеты v1 v1
    Только v2 v1 v2 v1
    v1 и v2 v1 Запросы v2, отчеты v1 и v2 v1

    А вот сравнительная таблица главных характеристик этих протоколов — Табл. 8.11.

    Сравнение возможностей MLDv1 и MLDv2
    MLDv1 MLDv2
    Типы сообщений ICMPv6 "запрос MLD" (130), "отчет MLD" (131), "итог MLD" (132) "запрос MLD" (130), "отчет MLDv2" (143)
    Виды запроса Общий и ограниченный группой Общий; ограниченный группой; ограниченный группой и источником
    Форма отчета Одна группа на сообщение Несколько групп на сообщение; каждую группу сопровождает режим фильтрации и список источников
    Адрес назначения в отчете и итоге Объявляемая группа "Все маршрутизаторы MLDv2"
    Выборы генератора запросов По адресу IP По адресу IP
    Подавление избыточных отчетов Да Упразднено
    Поддержка SSM и SFM Нет Да
    Кодирование интервала таймера Целочисленное С плавающей точкой, обратно совместимое

    Последним аспектом MLD, который мы не можем обойти вниманием, будет безопасность [§10 RFC 3810]. В какой степени этот протокол защищен от атак, и, с другой стороны, насколько опасны эти атаки? В отличие от ND, состояние стороны MLD зависит только от принятых ею сообщений MLD и не зависит от других внешних событий.

    Напротив, состояние ND может зависеть от любых пакетов IPv6. Так, маршрутизатор обращается к модулю ND по приходу каждого транзитного пакета.

    Поэтому единственный способ атаковать MLD — это подделка сообщений MLD. Главной мерой защиты от такой подделки будет изоляция канала от любых сообщений MLD извне. Действительно, по своему устройству MLD — стопроцентно внутриканальный протокол. Здесь можно пустить в ход весь арсенал доступных средств. Прежде всего, на выходе адрес источника сообщения MLD обязан быть внутриканальным за тем единственным исключением, что в отчете он может быть неопределенным. Тем не менее, отчеты с неопределенным адресом источника необходимы только для подготовки коммутаторов ЛВС на стадии SLAAC (§5.4.2) или DAD (§5.4.1), тогда как узлы IPv6 вполне могут игнорировать их на входе [§5.2.13 RFC 3810]. Действительно, сообщения ND, из которых состоят SLAAC и DAD, все равно не подлежат маршрутизации, и маршрутизаторам нет дела до членства слушателей в группах искомого узла и группе "все узлы канала". А позднее, когда узел назначит себе внутриканальный адрес, он повторит свои отчеты уже с определенным адресом источника. Поэтому на входе адрес источника в сообщении MLD должен быть внутриканальным, а иначе такое сообщение надо просто игнорировать.

    Далее, все сообщения MLD несут опцию "сигнал маршрутизатору", и это существенно облегчает выделение этих пакетов из общего потока. Следовательно, на входе все узлы обязаны убедиться, что пакет MLD снабжен этой опцией.

    Маршрутизаторам к тому же запрещено продвигать пакеты MLD куда бы то ни было. Продвижение MLD в другие каналы запрещено из соображений безопасности, а в тот же самый канал — ради устойчивости сети. Как мы уже отмечали, зацикливание группового трафика может иметь лавинный характер, когда каждая копия пакета рождает несколько копий следующего поколения и т.д. По этой причине MLD не защищают при помощи GTSM, а наоборот, устанавливают в пакетах MLD "предельное число шагов" равным 1. Ведь продвижение пакетов MLD в другие каналы уже заблокировано зонной архитектурой, тогда как продвижение в тот же самый канал она вполне допускает, как было сказано в §2.4.

    Справившись с внешними врагами, мы можем перейти к врагам внутриканальным, а они, конечно, более опасны. Главная угроза исходит здесь от подделки или просто несанкционированной отправки запросов MLD. Для начала злоумышленник способен провести атаку на выборы генератора. Для этого ему достаточно вести передачу запросов с как можно меньшего внутриканального адреса, например, FE80:: или FE80::1. Заполучив роль генератора запросов, или параллельно с этой атакой, злоумышленник может подавить всякую маршрутизацию группового трафика в данный канал. Для этого ему достаточно периодически высылать запрос, ограниченный никому не интересной группой. Как нетрудно убедиться, в этом случае законные маршрутизаторы будут вечно пребывать в состоянии пассивных наблюдателей, а слушатели никогда не получат повод послать свои отчеты. В результате законные маршрутизаторы будут обманным способом убеждены в том, что никаких слушателей на канале просто нет.

    На первый взгляд, такой атаке можно противопоставить модифицированную процедуру выборов, когда таймер OQPT перезапускается только по общему запросу. Однако у злодея в запасе есть ответный ход: слать общий запрос по адресу "все маршрутизаторы MLDv2", FF02::16. Маршрутизаторы обязаны принять и обработать такой запрос, как обычно [§5.1.15 RFC 3810]. В ответ злодею представим себе, что мы внесли поправку в протокол, и теперь общий запрос обязан быть адресован группе "все узлы", FF02::1. Тогда злодей разыграет козырь и пошлет такой запрос инкапсулированным в кадр Ethernet, где адрес назначения MAC равен 33-33-00-00-00-16. Благодаря фильтрации группового трафика на канальном уровне, в коммутируемой ЛВС такой кадр с большой вероятностью попадет только к маршрутизаторам MLDv2 Marc "vanHauser" Heuse. Recent advances in IPv6 insecurities. 27th Chaos Communication Congress. Berlin, 2010. http://events.ccc.de/congress/2010/Fahrplan/events/3957.en.html .

    Практический рецепт против такой атаки — это не принимать пакеты с адреса FE80:: , потому что это зарезервированный в §6.1 адрес anycast, и вручную назначить законным маршрутизаторам наименьшие внутриканальные адреса: FE80::1, FE80::2 и т.д. Однако у злодея снова появится шанс, как только маршрутизатор FE80::1 выйдет из строя, например, из-за атаки типа "отказ в обслуживании".

    В стратегической перспективе, достойный ответ атакам на MLD — это сертификация групповых маршрутизаторов аналогично SEND из §6.3.

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

    Другое дело, что бесконтрольное вступление хостов в группы небезопасно и может привести, например, к утечке конфиденциального трафика. Чтобы управлять членством в группах, понадобится инфраструктура аутентификации и авторизации слушателей MLD. Альтернативное решение проблемы состоит в сквозной защите группового трафика [RFC 3740], а это лучше отвечает фундаментальному принципу TCP/IP: не рассчитывай на помощь транзитных узлов, если без нее можно обойтись.

    Небезопасна и передача трафика группе, которую может вести любой источник. Режимы SFM и SSM сами по себе не делают ее безопасной, так как адрес источника можно подделать. Здесь тоже поможет система сквозной защиты, этакий "групповой IPsec".

    Вернуться к учебному плану