Pull to refresh

Мир, в котором IPv6 придуман хорошо

Reading time 19 min
Views 35K
Перевод статьи Avery Pennarun, одного из сотрудников Google, о том, почему современный интернет такой, какой он есть, об истории и предпосылках создания IPv6, а также о том, как был бы устроен идеальный протокол IPv6, почему это не так и как можно к этому идеалу приблизиться.

В ноябре прошлого года я впервые поехал на встречу IETF. IETF — это интересное место: кажется, что на треть оно состоит из тяжелой сопроводительной работы, на треть из расширения уже созданных вещей, а на треть — из безумных, далеких от реальности иследований (в этом месте Avery употребил словосочетание «blue sky insanity», образованное им от выражения blue skies research — прим. перев.). Я принимал участие в основном потому, что хотел посмотреть, как люди отреагируют на TCP BBR, который был впервые представлен. (Ответ: по большей части положительно, но с недоверием. Он казался слишком хорошим, чтобы оправдать надежды.)

Как бы то ни было, встречи IETF состоят в том числе из множества презентаций об IPv6, который должен был прийти на смену протоколу IPv4, составляющему основу интернета. (Кое-кто сказал бы, что замена уже идёт; некоторые — что она уже произошла.) Помимо этих презентаций про IPv6, присутствует большое количество людей, считающих его лучшим, величайшим из всего, и они уверены, что он вот-вот наконец придёт (В Любой Момент), а IPv4 — это просто большая куча хаков, которой предначертано умереть, чтобы интернет снова стал красивым.

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

Шины разрушили всё


Давным-давно жила-была телефонная сеть, которая использовала физическое переключение цепей. В сущности, это означало перемещение соединителей таким образом, что ваш телефон буквально оказывался подключен очень длинным проводом (уровень 1 OSI). А «выделенная линия» была тем самым длинным проводом, который вы брали у телефонной компании в аренду. Вы клали биты с одной стороны в этот провод, а из другого его конца они выходили спустя фиксированный промежуток времени. Вам не нужны были адреса, потому что на каждом конце была только одна машина.

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

Интернет (тогда так ещё не называвшийся) был построен поверх этих каналов. У вас была связка проводов, в которые можно засовывать биты и ловить с другой стороны. Если у одного компьютера два или три сетевых интерфейса, то он может, если его правильно проинструктировать, пересылать биты с одной линии на другую, а вы можете сделать что-нибудь гораздо более эффективное, чем отдельные линии связи между каждой парой компьютеров. И так появились IP адреса («уровень 3»), подсети и маршрутизация. Даже тогда, с этими каналами точка-точка, вам не нужны были MAC-адреса, потому что как только пакет оказывался в проводе, было только одно место, откуда он мог выйти. Вам нужны были IP адреса только для того, чтобы решить, куда он должен направиться после этого.

Тем временем, в качестве альтернативы были изобретены локальные сети (LAN). Если вы хотели у себя соединить свои компьютеры (или терминалы и мэйнфрейм), вы получали неудобство в виде множества интерфейсов, которые необходимо было иметь для каждого соединения в топологии «звезда». Чтобы снизить издержки на электронику, людям нужна была сеть типа «шина» (также известная как «домен широковещания», понятие, которое будет важно в дальнейшем), где множество станций могли быть просто подключены в один провод, и разговаривать с любым, кто подключен в него же. Это были не те же самые люди, кто строил интернет, поэтому они не пользовались IP адресами для этого. Они изобрели свою собственную схему («уровень 2»).

Одной из ранних локальных сетей типа «шина» была дорогая моему сердцу arcnet (я написал первый Linux драйвер arcnet и стихи arcnet в далеких девяностых, спустя много времени после того как arcnet устарела). Адреса arcnet уровня 2 были очень простыми: только 8 бит, устанавливаемых перемычками или переключателями DIP на тыльной стороне сетевой карты. Это была ваша задача как владельца сети — настроить адреса и убедиться, что у вас нет дубликатов, или в противном случае произойти может любая чертовщина. Это было слегка болезненно, но сети arcnet были обычно достаточно небольшими, так что это было всего лишь подобие боли.

Несколько лет спустя пришел Ethernet и решил эту проблему раз и навсегда, используя гораздо больше бит (на самом деле, 48) в адресах второго уровня. Это достаточно бит, чтобы вы могли присвоить отличный от других (шардированно-последовательный (тут, видимо, имеется в виду что первые три байта MAC-адреса закрепляются как диапазон за конкретным производителем — прим. перев.)) адрес каждому устройству, которое когда-либо было выпущено, и не иметь пересечений. И именно это они и сделали! Так появились MAC-адреса Ethernet.

Различные технологии LAN появлялись и уходили, включая одну из моих любимых, IPX (межсетевой (internetwork — прим. перев.) обмен пакетами, хотя у него не было ничего общего с «настоящим» интернетом), и Netware, который работал великолепно, до тех пор пока все клиенты и серверы были в сети из одной шины. Вам никогда не приходилось настраивать никакие адреса. Это было и красиво, и надёжно, и работоспособно. Практически, золотой век сетестроения.

Конечно, кто-то должен был это разрушить: большие сети компаний/университетов. Они хотели иметь столько подключенных компьютеров, что разделение 10 Мбит/с на единой шине между ними всеми стало узким местом, так что им понадобился способ иметь множество шин, и соединять между собой — «интернетить», если хотите — эти шины вместе. Вы, наверное, думаете, «конечно! Используйте Протокол Интернета (IP) для этого», правильно? Ха-ха, нет. Протокол интернета, всё ещё так не называвшийся, не был ещё достаточно взрослым и популярным в то время, и никто не воспринимал его всерьёз. Netware-over-IPX (и многочисленные на то время другие протоколы локальных сетей) был серьёзным делом, и как делает любой серьёзный бизнес, они изобрели свои собственные штуки, чтобы расширять набравший популярность Ethernet. Устройства на Ethernet уже имели адреса, MAC-адреса, которые были, наверное, единственным, о чем использовавшие различные протоколы LAN люди могли договориться, поэтому они решили использовать адреса Ethernet в качестве ключей для своих механизмов маршрутизации. (Вообще-то, вместо «маршрутизации» они называли это bridging и switching.)

Проблема, связанная с Ethernet адресами, заключается в том, что они назначаются последовательно на заводе, так что они не могут составлять иерархию. Это означает, что «bridging таблица» не так хороша как современная таблица IP маршрутизации, которая может содержать запись о маршруте до целой подсети сразу. Чтобы сделать эффективный мост (bridging), вы должны были запоминать, в какой шинной сети каждый MAC-адрес может быть найден. А человеки не хотели настраивать каждый из них руками, так что это нужно было выяснить самостоятельно. Если у вас было запутанное соединение сетей при помощи мостов, всё становилось немного сложновато. Насколько я понимаю, вот что привело к поэме об остовном дереве, и я, наверное, просто оставлю это здесь. Поэзия очень важна в сетевых технологиях.

Как бы то ни было, по большей части это работало, хотя и было немного запутанно, и у вас то там, то здесь случались широковещательные «потопы», а маршруты не всегда были оптимальны, и это всё почти невозможно было отлаживать. (Вы определенно не могли написать что-то вроде traceroute для мостов, потому что ничто из инструментов, которые нужны были, чтобы это заработало — такие как возможность настроить адрес на промежуточном мосту — не существуют в голом Ethernet.)

С другой стороны, все эти мосты были аппаратно оптимизированы. Железячниками была изобретена попросту целая система в качестве механизма, обманывающего софт, который понятия не имел о множестве шин и мостах между ними, чтобы тот работал в бóльших сетях. Аппаратный bridging означает, что мост может работать действительно быстро, настолько же быстро, как сам Ethernet. Сейчас это не звучит как что-то выдающееся, но в то время это было очень много. Ethernet был 10 Мбит/с, так что вы, возможно, смогли бы забить его, подключив несколько компьютеров сразу, но один компьютер 10 Мбит/с выдать не мог. В те времена это звучало бредово.

В любом случае, смысл в том, что bridging был месивом, которое невозможно отлаживать, но он был быстрым.

Интернет по шинам


Пока это всё происходило, те самые интернетчики взялись за работу, и, конечно, они не проморгали появление крутых дешевых LAN технологий. Я думаю, что это могло быть примерно то самое время, когда ARPANET переименовывался в Internet, хотя я в этом не уверен. Давайте скажем, что так и было, потому что история звучит лучше, когда её рассказывают уверенно.
В какой-то момент прогресс перешел от соединения отдельных компьютеров Интернет через дальние линии связи точка-точка к желанию соединять целые локальные сети вместе через соединения точка-точка. В общем, хотелось иметь «длинные мосты».

Вы могли бы подумать: «эй, да нет проблем, почему бы не построить мост на длинной линии связи и покончить с этим?» Звучит хорошо, но это не работает. Я не буду вдаваться в подробности, но вкратце проблема заключается в контроле перегрузки (к сожалению, почему-то нет русского перевода этой статьи на вики — прим. перев.). Страшная тёмная тайна Ethernet бриджинга — это допущение, что все ваши соединения работают примерно на одной скорости, и/или сильно недогружены, потому что у них нет механизма торможения. Вы просто выплёвываете данные так быстро, как можете, и ожидаете что они придут. Но когда ваш Ethernet работает на 10 Mbps, а ваше соединение точка-точка — на 0.128 Mbps, это совершенно безнадёжно. Другая проблема состоит в том, что выяснение маршрутов при помощи отправки по всем каналам, чтобы понять какой из них правильный — а таким образом бриджинг обычно и работает — слишком затратно для медленных соединений. А неоптимальная маршрутизация, досадная и в локальных сетях, где низкая задержка и высокая пропускная способность, на медленных и дорогих дальних каналах связи совсем отвратительна. Это просто не масштабируется.

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

И тогда они разработали «формат фрейма» для пакетов интернет через Ethernet (и arcnet заодно, и всех остальных типов LAN).

И вот здесь всё пошло наперекосяк.

Первой проблемой, которую надо было решить, стало то, что теперь, когда вы кладёте пакет в провод, стало совсем непонятно, какая машина должна его «услышать» и, возможно, переправить дальше. Если несколько интернет маршрутизаторов находятся в одном сегменте Ethernet, у вас не получится сделать так чтобы они все приняли пакет и попытались перенаправить его; это путь в сторону пакетных штормов и зацикленных маршрутов. Нет, вам нужно самостоятельно выбрать, какой маршрутизатор в шине Ethernet должен его подобрать. Мы не можем просто использовать поле IP адреса назначения для этого, потому что мы уже туда записали адрес получателя сообщения, а не адрес роутера. Вместо этого мы определяем желаемый роутер, используя его MAC-адрес в фрейме Ethernet.

Таким образом, чтобы настроить вашу локальную таблицу маршрутов IP, вы хотели бы иметь возможность сказать что-нибудь наподобие «отправляй пакеты к адресу 10.1.1.1 через роутер c MAC 11:22:33:44:55:66.» Это то самое, что вы хотели бы выразить. Важно! Назначение вашего пакета — IP адрес, но ваш роутер — это MAC. Но если вы когда-либо настраивали таблицу маршрутизации, вы возможно заметили, что никто так их не записывает. Вместо этого вы пишете: «отправляй пакеты к 10.1.1.1 через роутер на 192.168.1.1».

На самом деле, это только всё усложняет. Теперь ваша операционная система должна сначала найти MAC-адрес для 192.168.1.1, понять что это 11:22:33:44:55:66, и наконец собрать пакет с адресом назначения Ethernet 11:22:33:44:55:66 и адресом назначения IP 10.1.1.1. Адрес 192.168.1.1 нигде в пакете не указан, это просто абстракция для людей.

Чтобы сделать этот бесполезный промежуточный шаг, вам нужно добавить ARP (протокол разрешения адресов), простой не-IP протокол, задача которого — преобразование IP адреса в Ethernet адрес. Это делается широковещательным запросом всем в локальном сегменте Ethernet, с вопросом нет ли у них вот этого IP адреса. Если у вас есть мосты, они должны пересылать все ARP пакеты на все свои интерфейсы, потому что они являются широковещательными пакетами, а это как раз то, что и означает слово «широковещание» (broadcasting). В большой, занятой сети Ethernet со множеством связанных LAN, избыточные broadcast-ы становятся одним из ваших ночных кошмаров. Особенно плохо это в сетях WiFi. С течением времени, чтобы бороться с этой проблемой, люди придумали мосты/свитчи со специальными хаками, позволяющими избегать пересылки ARP до тех пор, пока это технически возможно. Некоторые устройства (в особенности точки доступа Wi-Fi) просто отвечают поддельными ARP ответами, чтобы помочь. Но это всё костыли, хотя подчас и необходимые.

Смерть из-за наследия


Шло время. Однажды (и вообще-то это заняло прилично времени) люди практически перестали использовать не-IP протоколы в Ethernet. Так что в основном все сети стали физическим проводом (уровень 1), со многими станциями на шине (уровень 2), шины объединяются при помощи мостов (попались! всё ещё уровень 2), а эти интер-шины соединены IP-маршрутизаторыми (уровень 3).

Какое-то время спустя люди устали вручную настраивать IP адреса в стиле arcnet, и захотели, чтобы они настраивались самостоятельно, в стиле Ethernet, ну, за исключением того, что было уже поздно делать это в стиле Ethernet, потому что а) устройства уже выпускались с адресами Ethernet, а не IP, б) IP адреса были лишь 32-битными, что недостаточно чтобы просто производить их бесконечно без пересечений, и в) простое последовательное назначение IP адресов вместо использования подсетей вернуло бы нас к началу: это был бы ещё один Ethernet, сделанный с нуля, а у нас уже есть Ethernet.

И тут появились bootp и DHCP. Эти протоколы, кстати, особенные — наподобие ARP (только они пытаются не быть особенными, технически являясь IP пакетами). Им необходимо быть особенными, потому что IP узел должен иметь возможность отправить их до того, как получит IP адрес, что конечно же невозможно, поэтому он просто заполняет IP заголовки по сути бессмыслицей (хотя и указанной в RFC), так что их можно спокойно отбросить. (Вы опознаете эти бессмысленные заголовки, потому что DHCP должен открыть raw сокет и заполнить их вручную; уровень IP в ядре не может это сделать.) Но никто не стремился радостно изобретать ещё один протокол, который не был IP, так что они сделали вид что это IP, и все были довольны. Ну, настолько, насколько возможно, когда вы изобретаете DHCP.

Я немного отвлёкся. Отличительная черта здесь следующая: в отличие от настоящих служб IP протоколам bootp и DHCP нужно знать об адресах Ethernet, потому что, в конце концов, это их работа — слушать ваши Ethernet адреса и присвоить вам IP адреса для дальнейшей работы. По сути это обращение протокола ARP, за исключением того, что мы не можем так сказать, потому что уже есть протокол RARP, который буквально и является «обратным ARP» (reverse ARP — прим. перев.). Вообще-то RARP работал вполне неплохо и делал то же, что и bootp и DHCP, будучи гораздо проще, но не будем об этом.

Смысл всего этого в том, что Ethernet и IP всё сильнее и сильнее переплетались. Сейчас они практически неотделимы. Сложно представить сетевой интерфейс (кроме ppp0) без 48-битного MAC-адреса, и сложно представить этот интерфейс работающим без IP адреса. Вы записываете вашу IP таблицу маршрутизации, используя IP адреса, но конечно вы знаете, что вы лжёте, называя роутер по его IP адресу; вы просто косвенно говорите, что вы хотите маршрутизировать через MAC-адрес. И у вас есть ARP, который ходит через мосты, но понарошку, и DHCP, который является протоколом IP, но на самом деле Ethernet и т. п.

Более того, мы всё ещё имеем и мосты (bridging), и маршрутизацию (routing), и они оба усложняются, в то время как локальные сети и интернет также усложняются и усложняются. Бриджинг всё ещё, в основном, аппаратный и определен IEEE, людьми, которые управляют стандартами Ethernet. Роутинг всё ещё, в основном, программный и определен IETF, людьми, контролирующими стандарты Интернет. Обе группы всё ещё пытаются делать вид, что другой группы нет. Сетевые операторы попросту выбирают bridging vs routing, опираясь на то, насколько быстро они хотят чтобы он работал и насколько сильно они ненавидят настройку DHCP серверов, которую они на самом деле ненавидят очень сильно, что означает что они используют мосты настолько, насколько это возможно и маршрутизацию — когда им приходится.

Фактически, мосты настолько вышли из-под контроля, что люди решили вынести решения, принимаемые на уровне моста, целиком на более высокий уровень (конечно же, обмен конфигурацией между мостами делается с использованием протокола поверх IP!), чтобы можно было ими централизованно управлять. Это называется программно-определяемая сеть (SDN). Это сильно лучше, по сравнению с тем, когда свитчам и мостам разрешено делать что им угодно, но это также фундаментально глупо, потому что знаете, что такое программно-определяемая сеть? IP. Это он буквально и есть, и всегда был SDN, которую вы используете для соединения сетей, ставших слишком большими. Но проблема в том, что IPv4 изначально было слишком сложно ускорить аппаратно, и в любом случае, он не получил аппаратного ускорения, а настройка DHCP — сущий ад, так что сетевые операторы просто научились, как соединять мостами всё большие и большие сущности. А сейчас большие датацентры попросту основаны на SDN, и вы могли бы не использовать IP в датацентре вообще с тем же успехом, потому что никто не маршрутизирует пакеты. Это всё просто одна большая сеть типа «шина».

Это, коротко говоря, бардак.

Теперь забудьте, что я всё это рассказал...


Хорошая история, правда? Хорошая. Теперь притворимся, что ничего этого не произошло, а мы вернулись обратно в 1990-е, когда большая часть всего на самом деле и случилась, но люди в IETF всё ещё делали вид что этого не было и «надвигающейся» катастрофы можно избежать. Это хорошая часть!

Я кое-что забыл упомянуть в этой длинной истории выше: где-то в этой цепи событий мы совсем перестали использовать шинные сети. Ethernet на самом деле больше не шина. Он только притворяется шиной. Попросту говоря, мы не могли бы заставить работать известный CSMA/CD по мере роста скоростей, так что мы вернулись назад к старой доброй топологии «звезда». У нас идёт пачка кабелей от свитча, так что мы можем протянуть один кабель от каждой станции к центру. Стены, потолок и полы заполнены большими, толстыми и дорогими связками кабелей Ethernet, потому что мы не смогли разобраться, как заставить шину работать хорошо… на уровне 1. Это несколько забавно, если задуматься. Если, конечно, вы находите забавными грустные вещи.

На самом деле, в порядке приступа безумия, даже WiFi — предельный случай «шинной» сети — правильно! — где буквально все разделяют одну и ту же среду открытого пространства, мы почти везде используем WiFi в режиме, называемом «инфраструктура», который эмулирует топологию гигантской «звезды». Если у вас есть две WiFi станции, подключенные к одной точке доступа, они не общаются друг с другом напрямую, даже когда могут хорошо «слышать» друг друга. Они отправляют пакет точке доступа, но адресованный MAC-адресу другого узла. Точка доступа затем отражает его в сторону узла назначения.

ПРИДЕРЖИТЕ КОНЕЙ ДАЙТЕ МНЕ ДЛЯ ВАС ЭТО ПОЯСНИТЬ. Есть здесь один подвох. Когда узел X хочет отправить что-то в интернет узлу Z, через IP роутер Y, через Wi-Fi точку доступа A, как выглядит пакет? Нарисуем картинку, что мы хотим:
X -> [wifi] -> A -> [wifi] -> Y -> [internet] -> Z

Z — адрес IP назначения, так что, очевидно, поле IP destination должно быть Z. Y — роутер, который, как мы узнали выше, указываем его Ethernet MAC-адресом в поле Ethernet destination. Но в Wi-Fi, X не может просто отправить пакет к Y, по разным причинам (включая то, что они не знают ключи шифрования WPA2 друг друга). Нам нужно отправлять в A. Вы возможно спросите, куда мы положим адрес A?

Не проблема! 802.11 имеет такую вещь как трехадресный режим. Они добавили третий Ethernet MAC-адрес в каждый фрейм, чтобы можно было говорить о настоящем Ethernet destination и промежуточном Ethernet destination. Поверх этого, есть ещё битовые поля, называемые «to-AP» и «from-AP», которые говорят вам, что пакет идёт от станции к точке доступа или от точки доступа к станции, соответственно. Но вообще-то они могут быть оба истиной, потому что так делают Wi-Fi повторители (ТД отправляет пакеты к ТД).

Кстати о повторителях! Если A — репитер, отправлять обратно к базовой станции B ему нужно по пути, который выглядит вот так:
X -> [wifi] -> A -> [wifi-repeater] -> B -> [wifi] -> Y -> [internet] -> Z

X->A использует трехадресный режим, но на A->B возникает проблема: Ethernet источника — X, а Ethernet назначения — Y, но пакет по воздуху пересылается от A к B; X и Y вообще не задействованы. Достаточно сказать, что есть такая штука как четырехадресный режим, и он работает именно так как вы могли подумать.

(В меш-сетях 802.11s есть режим, называемый шестиадресным, и примерно на этом месте я бросил попытки понять.)

Avery, мне обещали IPv6, а ты ещё даже не упомянул его


Ой-ой. Этот пост немного сошел с рельс, вы не находите?
Вот цель всего этого рассказа. Люди в IETF, когда придумывали IPv6, посмотрели на всё это безобразие — и, возможно, предсказали ещё большую неразбериху, которая должна была появиться, хотя я сомневаюсь, что они могли бы предсказать SDN и режимы повторителя WiFi — и сказали, погодите-ка минутку, стойте. Нам не нужно всё это дерьмо! Что если вместо этого мир вокруг нас работал бы вот так:
  • Никаких больше физических шинных сетей (уже готово!)
  • Никаких больше интерсетей уровня 2 (для этого есть третий уровень)
  • Никаких больше броадкастов (уровень два всегда будет точка-точка, поэтому куда вы бы отправили броадкаст? Заменим многадресными рассылками — multicast)
  • Никаких больше MAC-адресов (в сетях точка-точка очевидно, кто отправитель и кто получатель, а мультикаст вы можете сделать и на IP адресах)
  • Никаких больше ARP и DHCP (нет MAC-адресов, поэтому нет отображения IP адресов на MAC)
  • Никаких больше сложностей с заголовками IP (так что вы можете аппаратно ускорить маршрутизацию)
  • Никаких больше нехваток IP адресов (так что мы можем вернуться к маршрутизации больших подсетей)
  • Никакой больше ручной конфигурации IP адресов, кроме ядра (сети Интернет — прим. перев.) (а у нас есть столько IP адресов, что мы можем рекурсивно раздавать подсети по дереву оттуда)


Представьте, что мы жили бы в таком мире: WiFi репитеры были бы просто IPv6 роутерами. И точки доступа тоже. И Ethernet свитчи. И SDN. С ARP штормами было бы покончено. Каждую проблему маршрутизации можно было бы tracerout-ить. И что лучше всего, мы могли бы выбросить 12 байт (MACадреса источника и назначения) из каждого пакета Ethernet, и по 18 байт (источник/назначение/точка доступа) из каждого WiFi пакета. Конечно, IPv6 добавит нам дополнительные 24 байта адресов (по сравнению с IPv4), но вы выбросите 12 байт Ethernet, так что оверхед составит лишь 12 байт — сравнимо с использованием двух 64-битных IP адресов, если вы оставите заголовок Ethernet. Идея, что однажды мы сможем выбросить адреса Ethernet, помогла оправдать раздувание IPv6 адресов.

Это было бы красиво. Кроме одной проблемки: этого не случилось.

Реквием по мечте


Один коллега на работе сказал лучше всех: «слои всегда только добавляются, и никогда не исчезают».

Для всех этих чудес необходима возможность начать сначала и выбросить наследие, построенное к тому моменту. А это, к сожалению, по большей части невозможно. Даже если бы IPv6 достиг проникновения в 99%, это не значило бы что мы избавились от IPv4. А если мы не избавились от IPv4, мы не избавились от Ethernet адресов, или WiFi адресов. А если нам нужно соблюдать стандарты фреймов IEEE 802.3 и 802.11, мы никогда не сможем выбросить те байты. Поэтому нам всегда будет нужен протокол обнаружения соседей «IPv6 neighbour discovery», который просто является более сложным ARP. Даже хотя мы больше не пользуемся шинными сетями, нам всегда нужно будет некое подобие броадкастов, потому что так работает ARP. Нам нужно будет держать запущенным локальный DHCP сервер дома, чтобы наши устаревшие IPv4 лампочки продолжали работать. Нам всё ещё нужен будет NAT, чтобы устаревшие IPv4 лампочки смогли добраться до интернета.

И это ещё не самое худшее. Хуже всего то, что нам всё ещё нужна бесконечная мерзость в виде бриджинга второго уровня, из-за ещё одной ошибки, которую команда IPv6 забыла исправить. К сожалению, когда они разрабатывали IPv6 в 1990-х, идея была сначала запустить IPv6 — это должно было занять несколько лет — а затем работать над ним, когда IPv4 и MAC-адреса уже исчезнут, тогда эту задачу стало бы проще решить, а на тот момент всё равно ни у кого на самом деле не было по-настоящему «мобильных IP-устройств». То есть, что бы это вообще должно было значить — носить с собой ноутбук и втыкать его в Ethernet порты один за другим, в то время как файл загружается по FTP? Звучит тупо.

Приложение-убийца: мобильный IP


Конечно, имея пару десятков лет истории позади, сейчас мы знаем несколько примеров переносных компьютеров — ваш телефон — и подключения его к Ethernet портам беспроводным точкам доступа, одной за другой. Мы так всё время делаем. А с LTE, это даже в основном работает! С WiFi это работает лишь иногда. Неплохо, так?

Не совсем, из-за постыдного секрета Интернета: это всё работает только из-за bridging-а второго уровня. Маршрутизация интернета не работает с мобильностью, совсем. Если вы перемещаетесь по IP сети, ваш IP адрес меняется, а это ломает все открытые вами соединения.

Корпоративные WiFi сети обманывают вас, объединяя LAN целиком на втором уровне мостом, так что гигантский центральный DHCP сервер всегда отдаёт вам один и тот же IP адрес независимо от того, к какой корпоративной точке доступа вы подключились, а потом доставляет вам ваши пакеты, максимум с переывом на несколько секунд, пока мост переконфигурируется. Эти новомодные домашние WiFi системы со множественными репитерами/экстендерами делают то же самое. Но если вы переключаетесь с одной WiFi сети к другой пока гуляете по улице — если бы публичный WiFi был во всех магазинах подряд — то всё плохо. Каждая из них выдаёт вам новый IP адрес, и каждый раз, когда ваш IP адрес меняется, все ваши соединения рвутся.

LTE старается ещё усерднее. Вы сохраняете свой IP адрес (обычно IPv6 адрес в случае мобильных сетей), даже когда перемещаетесь на километры и многочисленные сотовые вышки передают вас от одной к другой. Как? Ну… они обычно просто туннелируют ваш трафик к центральной точке, где он весь соединяется мостом (хотя и через усиленную фильтрацию файрволлами) в одну супер-огромную виртуальную сеть второго уровня. И ваши соединения продолжают жить. Ценой большой сложности и действительно обескураживающего количества дополнительных задержек, которые они правда хотели бы убрать, но это практически невозможно.

Как заставить мобильные сети работать


Сноска 1
выясняется, что ничего в этом разделе не требует IPv6. Всё работало бы и с IPv4 через NAT, даже роуминг через несколько NAT-ов.


Ну ладно, это была длинная история, но мне удалось-таки вытянуть её из людей в IETF. Когда мы добрались досюда, до проблемы мобильных IP, я ничего не мог поделать, кроме как спросить. Что пошло не так? Почему мы не можем заставить это работать?

Выясняется, что ответ удивительно прост. Большой недостаток кроется в том, как была определена известная «четвёрка» (IP источника, порт источника, IP назначения, порт назначения). Мы используем эту четвёрку для идентификации данной сессии TCP или UDP; если в пакете указаны те же четыре поля, то он принадлежит этой сессии, и мы можем отправить её в тот сокет, который обслуживает сессию. Но четвёрка покрывает два уровня: сетевой (третий) и транспортный (четвёртый). Если бы вместо этого мы определяли сессии с использованием только данных четвёртого уровня, то мобильные клиенты работали бы превосходно.

Приведём короткий пример. Порт 1111 клиента X общается с 80 портом Y, поэтому он должен отправить четвёрку (X,1111,Y,80). Ответ приходит с (Y,80,X,1111), а ядро доставляет его в сокет, которых создал первый пакет. Когда X отпраляет ещё пакеты, обозначенные (X,1111,Y,80), Y отправляет их в тот же серверный сокет, и т. д.

Затем X меняет IP адрес, и получает имя, скажем, Q. Теперь он начинает отправлять пакеты с четвёркой (Q,1111,Y,80). Y понятия не имеет что это значит, и выбрасывает его. Тем временем, если Y отправит пакеты, обозначенные (Y,80,X,1111), то они потеряются, потому что больше нет X, готового их получить.

Представьте теперь, что мы бы пометили сокеты без привязки к IP адесам. Чтобы это работало, нам нужны гораздо большие номера портов (которые сейчас составляют 16 бит). Давайте сделаем их, скажем, длиной 128 или 256 бит, чем-то вроде уникального хэша.

Теперь X отправляет пакет Y с меткой (uuid,80). Заметьте, пакеты сами по себе всё ещё содержат информацию об адресах IP (X,Y), на уровне 3 — именно так они маршрутизируются до правильной машины. Но ядро не использует информацию уровня 3 для принятия решения о том, в какой сокет отправить пакет; оно просто использует uuid. Порт назначения (80 в этом случае) нужен только для начала новой сессии, чтобы определить к какой службе вы хотите подключиться, и может после этого игнорироваться или не приниматься во внимание.
Для обратного направления, ядро Y кэширует тот факт, что пакеты для (uuid) идут в IP адрес X, который является последним адресом, с которого приходили пакеты для (uuid).

Теперь представим, что X меняет адрес на Q. Он всё ещё посылает пакеты с тэгом (uuid,80) на IP адрес Y, но теперь эти пакеты приходят с адреса Q. Машина Y получает этот пакет и сверяет его с сокетом, ассоциированным с (uuid), замечает, что пакеты для этого сокета теперь приходят с адреса Q и обновляет кэш. Теперь пакеты в обратную сторону могут быть отправлены, с тэгом (uuid), в сторону Q вместо X. Всё работает! (С учётом мер, необходимых для предотвращения атак самозванцев).
Сноска 2
Кое-кто спросил, как могли бы выглядеть такие «меры предотвращения атак на соединения». Есть различные способы этого добиться, но самый простой — это сделать что-то вроде обмена SYN-ACK-SYNACK, который делается при старте TCP. Если Y просто доверяет первому же пакету от хоста Q, то атакующему слишком просто перехватить соединение X->Y путем отправки пакета в Y откуда угодно из интернета. (Хотя немного сложновато догадаться, какой 256-битный uuid надо подставить). Но если Y отправит обратно куку, которую Q должен получить, обработать и отправить обратно, то это докажет, что Q по крайней мере человек-по-середине, а не просто внешний атакующий (в любом случае, TCP вам тоже не гарантирует большего). Если вы используете зашифрованный протокол (такой, как QUIC), то рукопожатие также может быть защищено сессионным ключом.


Есть только один подвох: UDP и TCP так не работают, и слишком поздно их обновлять. Обновление UDP и TCP было бы сравнимо с обновлением IPv4 до IPv6; проектом, казавшийся простым тогда в 1990-х, но десятилетия спустя не завершенном и наполовину (и первая половина была простой; оставшаяся часть гораздо сложнее).

Хорошая новость в том, что возможно нам удастся обойти это ещё одним нарушением «расслоения». Если мы выбросим TCP — он всё равно уже достаточно стар — и вместо этого будем использовать QUIC поверх UDP, то мы сможем просто перестать использовать четвёрку UDP в качестве идентификатора соединения. Вместо этого, если номер порта UDP равен определенному значению, означающему «слой мобильности», то мы распакуем содержимое, которое может быть другим пакетом с правильным тэгом UUID, сверим его с правильной сессией и доставим эти пакеты в нужный сокет.

Есть ещё более хорошая новость: экспериментальный QUIC протокол уже, по крайней мере в теории, имеет правильную структуру пакета, чтобы так работать. Выясняется, что вам в любом случае нужны уникальные идентификаторы сессий (ключи), если вы хотите использовать stateless (без сохранения состояния) шифрование и аутентификацию, как делает QUIC. Так что, возможно с небольшими доработками, QUIC мог бы поддержать прозрачный роуминг. Каким стал бы такой мир!

Здесь всё, что нам нужно сделать — убрать все остатки UDP и TCP из интернета, а тогда у нас бы точно пропала необходимость в мостах второго уровня, на этот раз по-настоящему, и тогда мы могли бы избавиться от броадкастов и MAC-адресов, и SDN, и DHCP и всего остального.

Тогда интернет стал бы опять изящным.
Tags:
Hubs:
+55
Comments 59
Comments Comments 59

Articles