ICMPv6 и протокол NDP: служебные сообщения IPv6, обнаружение соседей и маршрутизаторов, замена ARP.
Какие функции объединяет ICMPv6 по сравнению с IPv4?
Что используют NS/NA в IPv6 вместо broadcast, как в ARP?
Что такое DAD (Duplicate Address Detection)?
Почему нельзя полностью блокировать ICMPv6 на фаерволе?
Какие типы сообщений NDP обеспечивают работу SLAAC?
В IPv4 у нас есть огромное количество служебных протоколов, без которых нам жизнь не мила. В первую очередь, конечно, это ARP, который обнаруживает канальные адреса соседей. Во вторую очередь это ICMP, который всякие служебные штуки делает для того, чтобы обнаруживать проблемы и понимать, как нам лучше отправлять трафик. Это DHCP, который решает задачу автоматической конфигурации хостов. И когда авторы протокола разрабатывали IPv6, они уже понимали, с чем приходится сталкиваться, и все служебные задачи решили вобрать в один большой протокол, который решает всё, что необходимо для работы IPv6. Протокол назвали ICMP — Internet Control Message Protocol. Это штатная часть протокола IP. Это не какой-то независимый протокол. Это служебный протокол, необходимый для работы IP.
Он не занимается передачей пользовательских данных, но он нужен для того, чтобы IP корректно мог передавать пользовательские данные. В IPv4 код вложения был единичка, в IPv6 код вложения протокола ICMPv6 — это 58. Формат пакета при этом такой же. У IPv6 ICMP и у IPv4 ICMP форматы одинаковые. Но поскольку задачи разные, протоколы считаются формально тоже разными. Этот протокол будет решать как те задачи, которые были классическими для ICMPv4, — оповещение об ошибках, оповещение о том, как лучше работать, управление заторами. Всё это сохранилось. Но дополнительно к этому ICMPv6 взял на себя задачи и других протоколов. Например, то, что раньше было в ARP — обнаружение канальных адресов соседей в Ethernet. ICMP тоже научился делать. Причём научился делать разными способами.
И для ICMPv6 уже не сильно актуально, есть там бродкаст или нет бродкаста. ICMPv6 заведомо работает в любой среде и корректно будет вести себя в самых разных канальных средах. Потому что в IPv4 у нас отдельно работал ARP в средах, где есть бродкаст. Отдельно работал, к примеру, NARP во Frame Relay. Отдельно там ещё ряд сценариев был, которые были несовместимы ни с ARP, ни с NARP. ICMPv6 работает одинаково на всех канальных средах. Механизмы могут быть разные, но результат будет именно такой, что именно ICMPv6 будет отвечать за обнаружение канальных адресов. Первое, что нас будет интересовать в ICMPv6, — это механизм Neighbor Discovery. Это обнаружение соседей — фича, которая в IPv4 поверх Ethernet решалась с помощью протокола ARP.
В IPv6 она работает следующим образом. У вас есть ICMPv6, который может обмениваться некоторыми сообщениями. Первая пятёрка сообщений, которые мы будем рассматривать, относится к механизму Neighbor Discovery, или NDP, или ND. Сообщение Router Solicitation, Router Advertisement — первая пара. Neighbor Solicitation, Neighbor Advertisement — вторая пара. И Redirect — это третья пачка, если хотите. Типы сообщений 133, 134, 135, 136, 137 — если вы помните, в заголовке ICMP у нас есть у каждого пакета тип и код. Типы, которые больше чем 128, будут характерны для IPv6. Давайте разберёмся, что эти две пары сообщений будут делать. Они нам будут больше всего интересны.
Router Solicitation и Router Advertisement — два сообщения, которые нужны для работы механизма обнаружения роутеров-соседей и получения от этих роутеров базовых настроек. Смысл заключается в том, что у вас есть роутер в сети, который может обслуживать трафик абонентов, находящихся в этой сети. И этот роутер периодически рассылает сообщения Router Advertisement. Он эти сообщения рассылает и в них рассказывает, как конечные узлы должны настроить себя, чтобы мочь пользоваться услугами этого роутера. И он по таймеру рассылает эти сообщения, например, раз в 10 секунд или раз в минуту. В этих сообщениях указывает, например, какую левую часть адреса следует использовать для получения глобального юникаст-адреса. И он будет рассказывать: возьми себе IP-шник 2001:db8:b16b:b5::/64.
Это IP-адрес сети, в которой надо придумать себе адрес. И когда узел получает такое сообщение, он говорит: я получил левую часть IP-шника, а дальше сейчас возьму себе, сгенерю Interface ID по Modified EUI-64 или как-то иначе, приклею одно к другому, и у меня получится полноценный IP-шник. Более того, я ещё и маршрут по умолчанию поставлю на того, кто мне эту RA прислал. Такой простенький механизм позволяет вместо протокола DHCP позволить узлам генерировать себе IP-шники автоматически, которые сразу же будут работать, которые сразу будут позволять установить шлюз по умолчанию на роутер-сосед. И фактически, взаимодействуя в интернете, вы таким образом можете уже сразу работать. Эти Router Advertisement рассылаются по таймеру, но если у вас какой-то узел только что включился, он может дождаться таймера, или он может послать внеплановое сообщение Router Solicitation — типа, роутеры, отзовитесь, мне прямо сейчас нужны настройки. И тогда RA пойдёт внепланово, вне таймера.
Эти пакеты будут идти: Router Solicitation будет идти с адреса «я не знаю свой IP-шник» на адрес ff02::2 — все роутеры. Вы хотите получить настройки, свой IP-шник не знаете, для того чтобы его получить, отправляете сообщение: все роутеры в канале, пожалуйста, ответьте срочно мне, или даже не столько мне, сколько вообще всем, какие настройки нужно использовать. Router Advertisement идёт на адрес вообще всех — ff02::1, но он уже идёт из-под link-local адреса этого роутера. Я, честно говоря, не знаю, какая была логика в том, что Router Solicitation идёт не из-под link-local адреса. На мой взгляд, логичнее было бы из-под link-local посылать такие сообщения тоже. Здесь нелогично, на мой взгляд. Но как есть. Запоминать это не нужно, просто достаточно понять логику.
Router Solicitation вы отправляете для того, чтобы вам внепланово прислали все настройки, а Router Advertisement — это плановая или внеплановая рассылка этих самых настроек. В Router Advertisement указываются, какие левые части адресов нужно использовать. В Router Advertisement используется назначение шлюза по умолчанию. Кто прислал, тот и будет шлюзом по умолчанию. А прислал кто? Вот этот link-local адрес. Поэтому в качестве шлюза по умолчанию этот узел будет назначать этот link-local адрес. И можно указывать дополнительные опции. Гипотетически, по стандарту вы можете, например, указать DNS. Но стандарт, который указывает, что DNS указать можно, он относительно свежий, и Windows, например, до сих пор не научилась. Поэтому, несмотря на то что этот механизм очень клёво выглядит, по факту он самостоятельно не является полноценно рабочим.
В современном мире мы без DNS жить в интернете не можем. И какие-то другие опции, типа сервера времени или что-то ещё. Захочется вам бездисковую рабочую станцию настроить, указать сервер, с которого надо загрузить конфигурацию — это всё в Router Advertisement не ходит. Поэтому Router Solicitation и Router Advertisement — это механизм, с помощью которого вы можете произвести базовую настройку узлов, но, как правило, дополнительно после базовой настройки какие-то конфигурации нужно получать ещё и отдельно. Просто отдельно висящие Router Advertisement в вакууме не дают вам всех настроек, которые нужны для работы в сети. Некоторые узлы могут работать с распознаванием DNS в Router Advertisement, но ещё раз подчеркну: Windows как основная корпоративная рабочая лошадка пока этого не умеет.
Второй механизм, который здесь нас интересует, — это Neighbor Solicitation, Neighbor Advertisement. Я думаю, вы уже догадались по названию: это запрос чего-то, а это рассылка чего-то. Смысл будет вот в чём. У нас есть два узла, которые обладают IP-адресами. Они, например, находятся в Ethernet, и для того чтобы отправить трафик в Ethernet, нам нужно указывать MAC-адрес соседа. В IPv4 мы бы, не долго думая, заорали на всю сеть: у кого из вас такой IP-шник, скажите свой MAC. Но в IPv4 мире это было допустимо, потому что когда IPv4 разрабатывался, проблемы с тем, что у нас слишком много бродкастов в сети, не стояло. А в IPv6, когда IPv6 разрабатывался, уже была проблема с бродкастовым трафиком. Его нужно как можно меньше отправлять в сети. Или вообще не отправлять. Поэтому для того чтобы взаимодействие в IPv6, позволяющее нам обнаружить канальные адреса соседей, не использовало бродкаст, авторы протокола IPv6 предложили, на мой взгляд, совершенно гениальную вещь.
Заключается она вот в чём. Если у вас есть какой-то IP-адрес, который необходимо кому-то узнать, и узнать по этому IP-шнику ваш MAC-адрес, или, выражаясь общим языком, канальный адрес. Давайте представим, что у нас здесь адрес fd00::2:1 по /64 маске. А здесь у нас fd00::2:2 по /64 маске. У нас есть узел, который говорит: я хочу попингить соседа fd00::2:2. На какой канальный адрес мне послать эти пинги? Я же не могу пинги послать бродкастом, правильно? Я, конечно, могу, но это будет некрасиво. Я хочу их послать как-то иначе. На какой-то MAC-адрес, который заставит большинство узлов в этой сети зажмуриться. Потому что здесь у нас не обязательно прямой провод. Здесь у нас свитч, и в этом свитче может быть ещё много-много абонентов. Я хочу, чтобы, даже если они получат такой кадр, они не читали его, чтобы они просто поняли, что им это неинтересно.
На уровне бродкаста, если отправлен FF:FF:FF:FF:FF:FF такой кадр, все должны будут прочитать, что здесь написано, все должны будут потратить ресурсы на чтение этого кадра. И это будет плохо. А как бы нам так извернуться и сделать так, чтобы эти узлы, если даже что-то и получат, скажут: нам это неинтересно, нам такой MAC-адрес получателя не нужен. А лучше всего, чтобы, если здесь свитч, он просто даже не отправлял эти данные на те порты, которые не содержат получателя. И эту штуку авторам протокола удалось реализовать. Придумано это было как раз для того, чтобы бороться с бродкастами. Смысл заключается в том, что когда у вас появляется любой IPv6 адрес, например, fd00::2:1 — это адрес, он любой в частности, — вы генерируете на основании этого адреса SNMA-адрес — Solicited-Node Multicast Address.
SNMA. И этот SNMA-адрес будет такой: ff02::1:ff00:1 — последние 24 бита IP-шника. 24 бита у нас — 00:00:01. Вот этот хвост — это от IP-адреса, а эта штука — это начало адреса, который генерируется для SNMA. Берём всегда эту часть и приклеиваем к ней хвосты своих IP-шников, которые у нас есть. И у нас получается наша мультикастовая группа Solicited-Node Multicast Address, соответствующая нашему IP-шнику. Группа ff02::1:ff00:1. Эти три нуля можно сократить, как понятно.
Здесь то же самое. Мы назначили себе IP-адрес fd00::2:2. Из этого адреса мы сгенерировали SNMA-группу ff02::1:ff00:2. Опять же, нули можно сократить. Магия. Когда нам хочется зарезолвить IP-шник соседа в MAC, мы не орём «у кого из вас такой IP-шник, скажите свой MAC». Мы отправляем мультикастовое сообщение на SNMA-группу, которая соответствует искомому IP-шнику. Мы знаем, что соседа зовут fd00::2:2. Мы из адреса соседа вычисляем его SNMA-группу и посылаем IP-пакет на SNMA-группу. В чём разница с бродкастом? Мультикаст — это не бродкаст.
Мультикаст позволяет тем, кто присоединился к этой группе, послушать такие пакеты, а тем, кто не присоединился — не послушать. Давайте представим, что здесь у нас толстый коаксиал. Мы отправляем мультикастовое сообщение на IPv6 адрес ff02::1:ff00:2. Это адрес группы — все узлы, чьи IP-адреса заканчиваются на двойку. Мы отправляем пакет на такой мультикастовый адрес. Этот узел говорит: у меня нет IP-шника, заканчивающегося на двойку, я читать такой пакет не буду. И этот говорит: я не буду. А вот этот говорит: я буду. Поэтому в пределах канала мы кадр доставили до всех, но все зажмурились, кроме одного, у кого действительно такой IP-шник есть. Более того, если говорить про реальное взаимодействие в сетях, здесь, скорее всего, не будет толстого жёлтого коаксиала, здесь, скорее всего, будет свитч.
И когда узел присоединяется к мультикастовой группе, например, вот такой или вот такой, он при этом ещё генерирует сообщения. Сообщения будут по протоколу MLD — Multicast Listener Discovery. Свитч может подслушивать, кто к каким группам присоединился, на каких портах. У нас есть один узел, и другой узел, и третий узел, и четвёртый узел — они на разных портах свитча сидят. Этот узел при присоединении к своей SNMA-группе сообщает: я присоединился к этой группе. Свитч запомнил, что кто-то присоединился к этой группе, запомнил, что на других портах никто к этой группе не присоединился. И когда такой мультикастовый пакет будет бежать по сети, доходит он до свитча, свитч его просто даже не отправляет на те узлы, которые к этой группе не присоединились. Он его отправит только до конечного абонента. Вот такой совершенно гениальный способ сделать так, чтобы запросы вида «у кого из вас такой IP-шник, скажите свой MAC» не доходили до тех, кому они не адресованы.
Они доходили бы только до тех, кто действительно является обладателем этого IP-шника. Вы не знаете, кто конкретно является обладателем этого IP-шника, но вы отправляете такой пакет, который доходит только до него. И он вам отвечает сообщением Neighbor Advertisement. Он говорит: ты тут спрашивал, у кого из вас такой IP-шник, скажите свой MAC — это я, вот у меня такой IP-шник, вот у меня такой MAC. В отличие от ARP не используется бродкаст. Вместо него используется мультикаст. Причём не на группу «вообще все», которая по смыслу как бродкаст, а на совсем другую группу. Neighbor Solicitation идёт с link-local адреса на SNMA-адрес, который нас интересует. Ответ идёт из-под link-local адреса на link-local адрес того, кто спрашивал. Есть ещё один нюанс: ARP у нас отвечал за определение конфликта адресов. Те самые, помните, Gratuitous ARP, Address Collision Detection и прочие штуки. Здесь это всё тоже есть.
И если вы используете IPv6, Neighbor Discovery, для определения конфликта IP-адресов, вы предполагаете, что кто-то может уже иметь ваш IP-шник. Вы его придумали, хотите проверить, он свободен или нет. В этой ситуации вы можете воспользоваться схемой Duplicate Address Detection. Вы отправляете пакет с пустого адреса, я не знаю свой IP-шник, на опять же SNMA адрес. И спрашиваете, у кого-нибудь есть такой адрес? Если у кого-то есть такой адрес, то он отвечает, у меня есть такой адрес. Он отвечает это из-под своего link-local-адреса, но он отвечает это на адрес вообще все. FF02::1. Почему не на link-local-адрес того, кто спрашивает, а потому что он его не декларирует. И эту схему можно использовать для проверки уникальности и link-local-адресов тоже. Вы придумали себе link-local-адрес, вы его пока ещё не используете, потому что мало ли вдруг кто-то уже такой использует. И вы сначала проверяете.
Говорите, у кого-нибудь такой link-local-адрес есть? SNMA для link-local-адресов тоже работает. И если вы спросили, у кого-нибудь есть такой link-local-адрес, и у кого-то уже есть такой link-local-адрес, то в этом случае вы не устраиваете конфликт адресов. Вы всё равно получите такой ответ, потому что FF02::1, он и на вас тоже придёт. Но при этом вы даже на секундочку конфликта не допускаете. Что касается особенностей при работе с ICMPv6, нужно будет не забывать, что ICMPv6 — это часть протокола IPv6. И вы можете случайно, если вы, например, настраиваете firewall, заблокировать работу ICMPv6. В IPv4 это было очень тяжело сделать. Там был ARP, который отвечал за обнаружение соседей, который не являлся частью IPv4. Он работал сбоку от него,
примерно на том же уровне, что и IP. У вас отправлялись и кадры ARP-шные, и кадры, содержащие IPv4, с Ethertype просто разными. 0x0800 для IPv4, 0x0806 для ARP. В IPv6 ICMP-шные сообщения упаковываются внутрь IPv6. Если вы настраиваете IPv6 firewall, вы можете заблокировать случайно или нарочно Neighbor Discovery. И у вас тогда очень много чего может поломаться. Если вы случайно или нарочно скажете, что ICMPv6 мы блокируем, обнаружения соседей у вас при этом не будет. И взаимодействия вообще не будет тоже. Это такой замечательный служебный протокольчик ICMPv6.