Multicast theory

Данная статья является как частью упорядочивания знаний по мультикасту, так и частью подготовки к CCIE R&S.
Как вы знаете, в старой версии CCNP RS внимание мультикасту уделялось, но он полностью убран из новой версии и встретить его можно разве что в CCNP SP. Это и радостно, ведь как и с Frame Relay ценность знания имеет место только по факту применения, а оно, скажем честно, и каждому десятому CCNP не попадётся на пути, и грустно, потому как куда проще взяться за изучение нового, если это требуется для выбранной сертификации, а этой мотивации вроде как насильно лишили.

Попробуем покрыть этой статьёй основную теоретическую часть.

Все команды приведены для оборудования на базе ОС Cisco IOS, как наиболее распространённого.

0. Введение
Раньше думал, что можно либо ненавидеть мультикаст, либо недолюбливать его, а потом попробовал погонять лабы и, знаете, втянулся.
Первый вопрос задаваемый мультикастом это “а зачем тебе меня учить? может оставим дружбы хоровод со мной ребятам занимающимся IPTV?”. Ну правда, как много вариантов применения мультикаста, кроме как IPTV-сети вы знаете? О! Есть ещё эти, когда звонишь в приёмную, где говорят: -“Подождите секундочку, сейчас переведу звонок” и включают тебя приятную музыку (Music on Hold (MoH)) – это тоже мультикаст.
Для начала посмотрите на эту древность (1997 год) – http://www.osp.ru/cw/1997/25/21761/
С тех пор (спустя 4 года, сентябрь 2001) появился даже отдельный RFC 3170, который раскрывает основные требования и сложности разработки программ, планирующих коммуникацировать посредством мультикаста. Однако, спешу вас расстроить, наше ассоцирование мультикаста с видео отнюдь не стереотип, а реальность – пример. Да, мы часто встречали его в работе многих популярных сетевых протоколов, типа OSPF, EIGRP, VRRP, HSRP, etc., но ведь там и особо задумываться не стоит о его работе, он просто есть.
Мы определённо не станем говорить о конкретных вариантах реализации мультикаст-сетей, равно как и о причинах появления такого варианта доставки трафика. Однако, перейдём от эфемерного описания мультикаста, к практической реализации в виде протокола маршрутизации и самой маршрутизации трафика, а так же взаимодействия с конечным клиентом.

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

Multicast
PIM
IGMP

2. Главное
Прежде всего хотелось бы напомнить, что Источнику вовсе не необходимы Получатели и даже их полное отсутствие не мешает слать валидный трафик на имеющийся аплинк. Так же напоминаю, что в основном классический мультикаст-трафик транспортно представляет собой не менее классический UDP, у которого в качестве destination IP указан один из адресов из специально зарезервированного под это дело блока – 224/4 (224.0.0.0 – 239.255.255.255). Однако, никто не мешает любому трафику доставлятся методом мультикаста, ведь мультикаст это именно метод передачи/доставки трафика. Таким образом, даже типичный ICMP Echo Request отправленный на 227.1.2.3 будет валидным мультикаст-трафиком, который будет, скорее всего, замечен тем кто ответственен за доставку и отправитель будет зарегистрирован как годный Источник!

Первым из двух важнейших понятий в основе мультикастинга лежит определение пути распространения мультикаст-трафика (Multicast Distribution Tree), определяемые, как мы уже привыкли, деревьями. Различают два варианта:

  • Source Tree – дерево строящееся по знакомому методу кратчайшего пути и результатом формирования такого дерева является Shortest Path Tree (SPT), обещающее, что реальный мультикаст-трафик пойдёт по кратчайшему пути, прямо от Источника (Source) к Получателю (Receiver). В этого рода деревьях Источник является как бы корнем (root) с кучей ветвей, где вся крона представлена Получателями.
    Используется нотация (S,G), где S это Source – IP-адрес Источника, а G это группа – IP-адрес, который этот источник использует для распространения некоторых данных. Например, (1.1.1.1, 230.1.2.1) означает, что источник 1.1.1.1 распространяет мультикаст данные используя мультикаст группу 230.1.1.1. Метод доставки по такому дереву так же называют SSM – Source Specific Multicast.
  • Shared Tree – дерево, в котором корнем является посредник, которому этот трафик не нужен, но он вроде как сказал, что знает все источники мультикаст-трафика в этом районе и обещал помочь любому, кто к нему обратится. Напрямую связан с таким понятием как Randezvous Point (RP). Т.е. RP и есть тот посредник и никто не говорит, что он должен быть единственным в этих местах. Используется нотация (*,G) [по-английски произносится “star comma G“], в первую очередь потому как IP-адрес источника получателю не известен и известен не будет – не за чем, ведь для него работает RP, а может даже несколько.
    С одной стороны путь трафика, всегда проходящего через RP, не всегда будет оптимальным, в отличие от варианта с Source Tree, но с другой конечному Получателю вовсе не надо знать данные Источника, ведь согласно нотации (*,G) ему без разницы кто конкретно будет Источником для этой группы, главное чтобы это была именно она.

Вторым важнейшим понятием является Reverse Path Forwarding (RPF) – один из вариантов защиты сети от петель, при этом очень простой.

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

multicast-rpf

 

Там где указана “галочка”, проверка RPF успешно пройдена и трафик отправляется через все прочие интерфейсы. Там где “крестик”, проверка не пройдена, так как согласно имеющейся таблице маршрутизации было обнаружено, чтобы пакет пришёл на “этот” интерфейс, а если придётся отвечать этому источнику, то уйдёт через “тот”.
Можно воскликнуть “А как же тогда Equal Cost Multipath?!”, ведь мы, как получается, не сможем выполнять балансировку трафика. Сможем, как минимум по аналогии с MHSRP, где вручную указываем, кто является ведущим для некой логической части трафика, мы можем иметь пачку RP, каждый из которых ответственнен за свой блок адресов групп. Так же популярна реализация балансировки с помощью туннелей.
Для проверки отрабатывания RPF есть отдельная команда, которая ждёт всего один аргумент – unicast-адрес Источника:

так, если мультикаст-маршрута нет в таблице маршрутизации

так, если мы указали не unicast-адрес

и так, если такой источник есть и мы указали его unicast-адрес

Проверить включена ли маршрутизация мультикаста можно следующей командой:

Для включения маршрутизатора в банду ответственных за мультикаст используется следующая команда:

А посмотреть таблицу маршрутизации так:

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

igmp-scheme

Ближайшую к Получателю L3-точку принято называть – last-hop router, но сейчас нас больше интересует по-настоящему ближайщая к клиенту точка. По факту это некий L2-порт, куда втыкается сетевой патч-корд прямо от обычной сетевой станции (PC, Laptop). Здесь и на каждом ином порту доступа нас встречает IGMP – Internet Group Management Protocol — протокол управления группами Интернета.
Именно с помощью этого протокола клиентская станция волеизъявляет желание получать некий мультикаст-трафик.
Принято брать во внимание, что версий IGMP аж 3 и все они довольно сильно отличаются, но так же принимая во внимание факт того, что IGMPv3 (RFC 3376) зарелизили больше 10 лет назад и операционная система Windows XP имеет нативную, полноценную поддержку онного (В системе Linux IGMPv3 был добавлен в версии ядра 2.5 – конец 2001 года. Для FreeBSD IGMPv3 был добавлен в версии 8.0 – 2009 год), давайте считать, что нет IGMP кроме третьего и пророка его, описываемого в следующем абзаце.
Первое, что встретится на пути запроса о присоединении к мультикаст-группе (IGMP Join) – функция IGMP snooping настроенная на портах коммутатора доступа. В принципе, в вики неплохо расписано, что это и зачем, но нам главное знать, что без этой фичи, коммутатор, получив мультикаст-поток свыше просто рассылает его на все порты доступа, не желая вникать, есть ли там Получатели. И речь не про служебный трафик, который обычно незначительных объёмов, речь про реальный такой трафик, который может утилизировать весомую часть канала передачи данных. Для примера, представьте 48-портовый коммутатор уровня доступа, на 40 портах которого есть Получатели IPTV, т.е. прямо сейчас смотрящие National Geographics, AXN SciFi, FashionTV и прочие. Общая ширина канала, утилизируемая на аплинке будет (при 40 одновременных ТВ-каналах) порядка 90Мбит/сек. И весь этот поток рассылается методом broadcast по всем портам доступа, хорошо если из такой пачки хотя бы часть является действительно желанным трафиком, но у нас как минимум 8 портов, на которых получателей нет, а 90Мбит туда честно сливаются! Полагаю, понятно – IGMP Snooping это не просто возможная фича, а необходимая.
Архитектурно работу можно сравнить с DHCP Snooping – коммутатор, являясь устройством специализирующимся на канальном уровне, тихонько подглядывает в трафик, интересуясь заголовками сетевого уровня и ведёт у себя небольшую базу успешных запросов. Только в базе DHCP Snooping мы видим таблицу арендованных адресов в виде набора “MAC / IP / Lease time / Interface”:

а в базе IGMP Snooping видим таблицу “Group / Version / Interface ”

IGMP Snooping так же делает такую хорошую вещь как Last member query, позволяющую снизить количество IGMP-трафика до last-hop router, реально передавая IGMP Leave только от последнего Потребителя определённой мультикаст-группы. Т.е. из 50 Получателей 48 переключили телеканал, что на сетевом уровне означает послать запрос о выходе из списка потребителей – IGMP Leave, но наш коммутатор доступа с IGMP Snooping не посылает его выше, ведь здесь есть ещё Потребители и в реальности это никак не скажется на работе last-hop router, который всё ещё продолжит слать поток вниз – два Получателя ждут.
По умолчанию, IGMP Snooping включен на всех портах всех VLAN коммутатора доступа на базе Cisco IOS:

Рассматривая IGMP Snooping мы рассмотрели на каких этапах возникают и как называются два из четырёх типов пакетов IGMP – Join (присоединение, т.е. запрос трафика определённой группы) и Leave (покидание, т.е. отказ отныне получать трафик определённой группы). Остались ещё IGMP Report и IGMP Query.
Report шлёт Потребитель (на 224.0.0.22), Query шлёт last-hop router(на 224.0.0.1, каждые 60 секунд). Потребитель уведомляет last-hop router о том какие группах слушает, last-hop router спрашивает у Потребителей какие группы те слушают. Зачем этим занимаются оба? Ну, по умолчанию мультикаст-маршрут (S,G) живёт почти 3 минуты, а за это время можно прогнать очень много трафика, который на самом деле никому не нужен. Потребитель может и хотел бы гарантированно посылать IGMP Leave когда положено, но пьяный электромонтёр может быть против корректного выхода из группы.

4. Протокол PIM
Как стало понятно, мультикаст-трафик старается существовать отдельно от unicast и для этого использует свои деревья, свою таблицу маршрутизации и даже свои протоколы маршрутизации. Так как в рамках мультикаст сетей существуют уникальные понятия, не свойственные другим, необходимо наличие в арсенале протоколов, которые позволят такими сетями управлять и успешно маршрутизировать в них трафик. Таких конечно не один и существуют как минимум Distance Vector Multicast Routing Protocol (DVMRP), Multicast Open Shortest Path First (MOSPF) и Core Base Trees (CBT), но принято считать, что выйграл войну поныне наиболее широко распространённый протокол Protocol Independent Multicast (PIM) (wiki eng|rus) и как можно узнать, несмотря на то, что он называется “независимым”, ни о какой работе не получится говорить, если между узлами отсутствует базовая IP-связность, средствами любого IGP. Может возникнуть небольшая путаница оттого, что PIM это одновременно и имя для семейства протоколов, куда одноимённый протокол входит.
Если вы знакомы с такими динамическими протоколами маршрутизации, как, хотя бы RIP, то поймёте, что означает понятие установление соседства. То самое явление, когда некая логически-осмысленная и руками-настроенная группа пытается найти идентичного по протоколу соседа, с которым можно будет расширить влияние и управлять этими территориями.
PIM-enabled маршрутизаторы рассылают PIMv2 hello-пакеты (All-PIM-Routers) на адрес 224.0.0.13, TTL=1, по умолчанию каждые 30 секунд и время ожидания перед признанием соседа мёртвым равно hello-time * 3.5 – 105 секунд.
Значение великовато для современных сетей, но легко меняется, в том числе на миллисекундные значения. Настройка происходит на уровне интерфейса:

Различают несколько режимов работы PIM, плотно связанных с описанными выше столпами:

  • PIM Dense-Mode (PIM-DM) – Уплотненный режим.
    Описание протокола PIM-DM находится в RFC 3973.
    “PIM-DM assumes that when a source starts sending, all downstream systems want to receive multicast datagrams.”
    Для работы использует Source Tree, которые строит путём лавинного распространения (flood) мультикаст-трафика, с последующим урезанием (prune) участков, где не обнаружено Получателей для указанной группы. Попытки найти ещё неизвестных Получателей повторяются через каждые State Refresh Timer(SRT), равный по умолчанию 3 минуты и все, всё ещё безучастные повторяют свой Prune. Для каждой (S,G) тикает свой SRT.
    multicast-sr-pruneВообще говоря этот режим практически не используется, ввиду того что сам режим и является недостатком. Т.е. он отлично бы подошёл для использования в мультикаст сетях, где небольшой набор источников распространяет трафик для большого набора получателей, изначально расположенных в этой сети в ожидании онного и только. Не то чтобы служебный PIM-трафик был хоть сколько-нибудь значительным, но в современных компьютерных сетях по сравнению с сетями когда создавался PIM, значительно выше стали цениться управляемость и архитектурный подход, благодаря которым распространение вида “просто отправлю, вдруг кто захочет” не является допустимой нормой.
    Кстати, примером сети, где такой режим вполне сгодится, является сеть компании-трейдера, торгующей на финансовых биржах, типа NASDAQ. Представьте, сотни(тысячи?) компьютеров за которыми сидят реальные трейдеры и всем им необходимо отправлять реал-тайм график фьючерсов на нефть марки BRENT, плюс десяток-два других, а на группы плазменных панелей в каждом кабинете больше 50 м2 ещё и графики всех валют.
    В IOS данный режим явным образом указывается на интерфейсе:
  • PIM Sparse-Mode (PIM-SM) – Разреженный режим.
    Описание протокола PIM-SM находится в RFC 4601.
    На самом деле, даже описанный выше случай с трейдерами не спасёт режим PIM-DM, ведь современное оборудование легко пережёвывает гораздо больше Потребителей, при этом управляемо и масштабируемо. Как раз используя PIM-SM.
    Для работы использует Shared Tree, располагая в нужных местах RP, что значительно повышает эффективность распространения мультикаст-трафика. Наиболее популярный режим, хотя бы потому, что позволяет создавать отказоустойчивые решения.
    Базовая настройка режима представляет собой всего две команды:
    В этом варианте, на каждом интерфейсе, который планируется задействовать в маршрутизации мультикаст, указывается режим и в privileged exec mode статически указывается RP. Данный RP должен быть указан на всех участниках, в том числе на самом RP.
    Вариант не подразумевает отказоустойчивости и в результате отказа явноуказанного RP, мультикаст трафик передаваться не сможет. К счастью, существует целая плеяда вариантов эту отказоустойчивость обеспечить.

    1. Auto-RP
      Принято утверждать, что метод сей является Cisco proprietary, но тогда я не знаю как объяснить это.
      Метод заключается в рассылке всеми претендентами на престол специального объявления. Претенденты называются Candidate RP (C-RP) и по умолчанию отправляют RP-anounce на 224.0.1.39 каждые 60 секунд. Включить рассылку можно так:

      ip pim send-rp-announce <interface> scope <TTL> 


      В это же время специальные агенты – Auto-RP Mapping Agent, коллекционируют информацию обо всех претендентах и начиинают рассылать RP-discovery (224.0.1.40), в котором называют того, кто был выбран RP для того или иного блока мультикаст-групп, согласно правилу большего IP. Настройка Mapping agent производится следующей командой:

      ip pim send-rp-discovery <interface> scope <TTL>

      Передача всех объявлений происходит по методу dense, что разумно.

    2. Bootstrap Router
      Auto-RP мог стать популярным, но в наборе PIMv2 появился полностью стандартизированный механизм – Bootstrap router (BSR) [RFC5059].
      Так как механизм является частью PIM, первое отличие от Auto-RP заключается в том, что рассылка производится только через PIM-enabled интерфейсы, методом hop-by-hop, когда PIM-маршрутизатор получив BSR-discovery, генерирует копию на все свои PIM-enabled интерфейсы. Благодаря подконтрольному распространению отпадает необходимость ограничивать область распространения с помощью TTL.
      Выборы RP происходят в два этапа:
      – каждый маршрутизатор, настроенный как BSR распространяет BSR-candidate по PIM-интерфейсам и ожидает таких же сообщений от соседей. Каждый маршрутизатор, приоритет которого оказался ниже приоритета соседа, прекращает рассылку. так происходит до тех пор, пока в мультикаст-домене ни окажется тот самый единственный BSR. Выборы могут показаться похожими на выборы STP root – так и есть. Настройка маршрутизатора как BSR-кандидата:

      ip pim bsr-candidate <interface> [priority]

      – когда BSR определён, каждый маршрутизатор, настроенный как RP-кандидат, используя unicast отправляет BSR сообщение (каждые <interval> секунд), в котором указывает свой IP и список мультикаст-групп, за которые он хотел бы отвечать. BSR выбирает RP для каждого набора мультикаст-групп, в первую очередь ориентируясь по наименьшему priority. Настройка маршрутизатора как RP-кандидата:

      ip pim rp-candidate <interface> [group-list ] [priority]

      Разумеется для всех объявлений можно настраивать интервал между отправками.
      Важным, в плане актуальности, отличием BSR от AutoRP является поддержка IPv6 адресов.

    3. Anycast-RP
      Самый “свежий” из вариантов обеспечения отказоустойчивости, организованный на тонкости таблицы маршрутизации, позволяющей использовать один IP-адрес на разных сетевых узлах. Именно этот адрес и будет использоваться в качестве RP, позволяя как статически указывать его на всех остальных узлах, значительно снижая сложность мультикаст-сети, так и сделать его претендентом и победителем гонки AutoRP/BSR. Обычно для этого IP делается ещё один отдельный loopback, помимо уже имеющегося loopback, который традиционно выступает как IP явно индентифицирующий данный узел.
      Настроенные таким образом RP требуют согласованной работы, для чего используется протокол Multicast Source Discovery Protocol – MSDP [RFC3618]. Этот же протокол используется для согласованной работы RP в многодоменной мультикастовой среде, зачастую вместе с MP-BGP, который вполне себе умеет анонсировать мультикаст-префиксы [address-family ipv4 multicast].
  • PIM-SSM
    Наконец оставим старый PIM-DM, популярный PIM-SM, со всеми его трудостями и перейдём к режиму который добавит ещё путаницы.
    Если взять метод построения мультикаст-дерева – Source Tree, отрезать вещательные минусы PIM-DM и повышающие сложность сети потребности PIM-SM, мы получим режим PIM-SSM [RFC3569].
    Работа режима напрямую связана с IGMPv3, который позволяет явным образом указывать от каких источников мы желаем получать трафик мультикаст-группы, используя нотацию (S,G). Такое изменение, в первую очередь связано с желанием использовать мультикаст в глобальных сетях, где один и тот же адрес мультикаст-группы могли бы использовать тысячи несвязанных между собой узлов. В этом случае пользовательская станция сама должна знать IP-адрес источника, ведь нет RP на который можно бы было положиться. Но прелесть в том, что используя явное указание везде где можно мы делаем уникальной записью каждую пару “Получатель + (S,G), что позволяет маршрутизатору отделять всех от всех.
    Под данный мультикаст выделен диапозон 232.0.0.0/8.
    Может работать как сам по себе, так и в паре с PIM SM, который отдельно управляет маршрутизацией остальных групп, что обычно и делают.
    Примером когда такой режим может быть актуален в глобальной сети Интернет является хотя бы youtube.com, который одновременно генерирует огромное количество копий трафика неоптимальным для этих задач методом unicast.
    Конфигурация дополнительно к уже имеющемуся PIM Sparse-mode проста и незатейлива:

  • PIM-BiDir
    Сложные и в значительной степени надёжные схемы сети пораждают необходимость передавать трафик в самых разных векторах направления, что для типичного sparse-mode не является приемлимым. Сети, где имеется необходимость двунаправленного мультикаста ещё называют many-to-many multicast network. Т.е. значительное количество источников, отправляет некоторое количество мультикаст-групп значительному количеству получателей.
    С понятием PIM-BiDir часто так же связывают явление называемое Phantom RP, когда RP указан везде и так как положено, но в реальности сетевого узла с этим IP не существует и функция RP выполняться не будет, некому ведь. Но, никто не мешает иметь вполне себе валидный RP в сетях с режимом PIM-BiDir. Однако сам факт того что PIM-BiDir использует Shared Tree обязывает иметь некий RP, существует он в реальности или нет.
    Я намерено не упомянул о том как сходятся RP с Источником, когда используется режим PIM-SM. А сходятся они используя именно Source Tree. Вы ещё не запутались? Т.е. PIM-SM это не только Shared? Да, однажды и я запутался, распутался, ещё раз запутался и ещё раз распутался. Так вот, чтобы RP узнал о некотором Источнике, до которого может быть n-ое количество хопов, используется схема когда ближайший к Источнику PIM-enabled маршрутизатор, используя Soure-дерево, отправляет в сторону RP пакет PIM Register, в котором содержится адрес мультикаст-группы или нескольких, по которым этот Источник вещает. Вернувшись к PIM-BiDir вспомним что RP ведь может и не быть. Что делать? Кому слать RIM Register? Для этого всё же выполняется выбор специальной точки – Designated Forwarder (DF). Работа заключается в том, что все PIM соседи, обязательно настроенные для BiDir,  обмениваются информацией о их удалённости (дистанции) от RP [несмотря на то что, IP-адреса который настроен как RP может и не быть в сети, он (IP) всё же входит в какую-то подсеть и дистанция в этом случае считается именно до неё]. Маршрутизатор объявивший минимальную метрику выбирается в качестве DF и именно к нему отправляют PIM Register.
    В общем случае режим включается довольно просто:

5. Адресация
Последнее о чём хотелось бы упомянуть это управление адресами.
Как вы успели заметить, настройка мультикаст-домена или несколько смежных представляет собой вовсе не сверхсложный и весьма удобочитаемый набор команд. Самым сложным в настройки является распределение и управление блоком мультикаст-адресов, которые указываются с помощью standard ACL и удобно дробятся на части, распределением на несколько RP.
Весь блок 224/, заранее разделён на части, чтобы видение сетевого администратора несколько стандартизировать.
Так, например блок 224.0.0.1 – 224.0.0.255, называется Local scope address и никогда не маршрутизируется. Именно эти мультикаст-группы используются для служебного трафика между сетевыми узлами административного домена.
Блок 224.0.1.0 – 238.255.255.255 называется Global scope address и пригоден для использования в Интернете, да, собственно везде, где вам того захочется. Это те адреса, которые используют Источники.
А блок 239.0.0.0 – 239.255.255.255 считается Limited/administratively scoped [RFC2365], адреса из которого, наравне с TTL, могут использоваться для контроля границ мультикаст-домена.

Для всех кто заинтересован в IPv6, прошу прощения, что этой части почти не упоминается.
Вы можете ознакомиться зарезервированными IPv6 multicast адресами по этой ссылке.

6. Источники

SHARE: Tweet about this on TwitterShare on FacebookShare on VKShare on LinkedInShare on Google+Email this to someone
  • Roman Gorkusha

    Сюда еще неплохо бы более подробную часть про igmp. И про multicast на уровне L2 – как разные свитчи работают с мультикастом, детли работы igmp на разных платформах, igmp-snooping quierier… Оно хоть и не упоминается в CCIE-R&S, но нужно в жизни )

    • Sk1f3r

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