Практика IPv6

    Краткое содержание:
    • Новости облака Селектел:
      1. IPv6 во всех шаблонах включен по умолчанию. Все новые машины устанавливаются с настройками IPv6 и сразу после установки доступны по своему IPv6 адресу.
      2. В списке шаблонов доступна openSUSE 12.3 с ядром Linux 3.7-xen
    • Рассказ про практические проблемы, с которыми мы столкнулись при эксплуатации IPv6.


    С openSUSE всё понятно, так что обсудим последствия IPv6.



    Оригинал статьи в корпоративном блоге Селектел.

    IPv6 для облачных серверов


    Для всех виртуальных машин в пулах Санкт-Петербург (1) и Санкт-Петербург (2) при установке новых виртуальных машин и переустановке существующих поддержка IPv6 включается по умолчанию (и является предпочтительным протоколом для исходящих соединений). IPv4, разумеется, остаётся и работает. Раньше мы IPv6 выдавали, но по умолчанию не включали.

    Зачем это нужно? Честно сказать, сейчас львиная доля Интернета работает на ipv4. Отдельные островки живого IPv6 есть в Азии, плюс несколько крупных сайтов (таких, как google.com, vk.com, facebook.com) отвечают по IPv6. Домашние пользователи в России практически все работают только по IPv4.
    Вот более-менее актуальная информация о том, у кого из провайдеров России есть IPv6: version6.ru/isp

    Однако, переход на IPv6 должен произойти — и чем больше сайтов будет готово к работе с IPv6, тем легче и спокойнее произойдёт переход, так что это инвестиция в будущее.

    Что означает появление IPv6 с практической точки зрения для конкретно взятого облачного сервера?

    • Пользователи сайта, у которых IPv4 как ходили по IPv4 адресам, так ходить и будут
    • Пользователи, у которых есть IPv6 смогут зайти на IPv6 адрес сервера только если этот адрес прописан в DNS (AAAA-запись)
    • Исходящие соединения с сервера на IPv4-only узлы будут идти с использованием IPv4
    • Исходящие соединения с сервера на узлы с IPv6 и IPv4 адресами будут идти с использованием IPv6 адресов


    У сервиса облачных серверов есть неофициальное соревнование с сервисом облачного хранилища: у кого будет больше IPv6 трафик. До сегодняшнего дня облачное хранилище выигрывало — но есть надежда перетащить флаг на свою сторону.

    Основное, куда пойдёт IPv6 трафик:
    • Службы google
    • mirror.selectel.ru и mirror.yandex.ru (Яндекс включил IPv6 для mirror, но не включил для поиска, увы), да и на остальные мирроры дистрибутивов, которые в большинстве своём поддерживают IPv6
    • Если вы настроите AAAA запись для домена, то гугловый робот придёт к вам по IPv6. Если у вас есть веб-краулер или fetch на чужие сайты, то он тоже будет предпочитать IPv6.


    IPv6 в реальной жизни


    Существует расхожее менение о том, что все совеременные ОС поддерживают IPv6 из коробки, там всё работает и там всё просто и легко. Существует и обратное мнение, мол, всё будет глючить и бибикать. Как показывает практика, оно из коробки работает и бибикает. Или, наоборот, глючит и всё легко и просто. Другими словами, работает, но граблей с собой приносит изрядное количество, но жить можно.

    Ниже рассказ про некоторых из них.

    Наличие dual stack означает, что в каждом месте, где у нас явно или неявно (например, исходящие соединения) появляются IPv6 нам надо подумать. Таких мест много — как только у вас появился dual stack, у вас тут же все утилиты (начиная с wget и заканчивая ssh) начинают ходить по IPv6 над ipv4. Иногда молча, иногда молча отваливаясь. Некоторые вполне себе уважаемые компании прописывают себе AAAA для домена, но забывают настроить веб-сервер. Получается казус.

    Postgresql и IPv6


    PostgreSQL всегда славилась отличным набором типов данных. Начиная от геометрических объектов и заканчивая деньгами. IP-адреса так же в комплекте. Есть два типа: inet и cidr. Оба хранят специальным образом ip-адрес и маску. Разница в том, что inet хранит адрес узла (то есть допустимы ненулевые биты в нулевой зоне под маской), а cidr хранит сети (то есть адреса хостов в нулевой зоне не допустимы). По сути это одно и то же, но если попытаться записать адрес хоста в cidr, будет выдана ошибка из-за того, что условия не выполнены.

    Тип один для IPv4 и IPv6. Размерность определяется автоматически.

    Допустимые операции: сложение со скаляром, вычитание скаляра, всякого рода сравнения in, not in, перекрывающихся диапазонов, равенства и т.д. В случае с ipv4 это позволяло реализовать всё, что хочешь. Например, если мы делаем распределение ipv4, то мы просто берём max_ipv4 и говорим +1, а потом проверяем, попадает ли это в диапазон разрешённый для выделения.

    С IPv6 ситуация другая. Адреса выделяются /64, а в случае выделения маршрутизируемых сетей — /48. Для того, чтобы получить следующую /48 надо взять последнюю выделенную и сделать max_ipv6 + 1208925819614629174706176 (2128-48=280). Всё хорошо, но bigint в postgres — это всего навсего 64-битное число, которое даже 264 хранить не может, не говоря уже про 280. Другими словами, попытка такого «плюса» вызывает ошибку из-за слишком большого размера прибавляемого скаляра. Итого — в postgres полностью сломан механизм управления IPv6 сетями. Наше временное решение — реализация inet/cidr в persist'е (библиоткека haskell для работы с СУБД) и реализация самостоятельной математики. В апстрим проблема зарепочена, решения (по состоянию на 9.2) пока что нет.

    Несколько IPv6 на интерфейсе, DAD и nginx


    Единственным методом прописать на интерфейсе несколько IPv6 адресов является использовнаие post и pre секций c использованием ifconfig. Адрес на интерфейсе появляется, но не сразу, так как начинает работу DAD — Duplicate address detection. В самом простом изложении компьютер спрашивает «есть у кого мой адрес?» и ждёт ответа. Этот протокол позволяет избежать появления повторяющихся адресов, однако, ifconfig заканчивает свою работу по конфигурированию интерфейса до завершения ожидания в DAD.

    В результате, если nginx (или любой другой сервер) имеет настройку на соответствующий адрес, а не на звёздочку, то при попытке сделать там listen, сервер получает ошибку. И не слушает. Это чистой воды гонка условий, которые можно заметить только в процессе загрузки сервера (т.к. в остальных случаях DAD успеет отработать до запуска/перезапуска сервера). Отладка этой проблемы была весьма и весьма неприятной.

    Точки в IPv6-адресе


    Не верите? Думаете, что только двоеточия?
    ::192.168.1.1 — валидный IPv6 адрес, хоть и не используется в интернете.

    Так сказать, поздравляю всех, кто надеялся, что регэкспы для IPv6 адресов в интерфейсах будут простыми…

    routing advertisement && forwarding


    Linux, если ему говорят, что он маршрутизатор, перестаёт доверять routing advertisement. В частности, если узел получает маршруты только из RA, то после включения маршрутизации, он перестаёт доверять оным RA (то есть ipv6 сеть перестаёт работать). Если хочется иметь и RA, и форвардинг, то значение RA надо выставить в 2 (net.ipv6.conf.eth0.accept_ra=2). Если запущен сервис анонсов (radvd), то следует аккуратно различать, чьим анонсам доверяем, а чьим — нет. Довериться своим собственным анонсам — это нелепо и не работает.
    Селектел 71,39
    Selectel — крупнейший российский IaaS-провайдер
    Поделиться публикацией

    Вакансии компании Селектел

    Комментарии 27
    • +3
      позор Фейсбуку, у которого IPv6 нет!
      Как это нет?

      $ dig aaaa facebook.com +noall +answer
      
      ; <<>> DiG 9.9.2-P1 <<>> aaaa facebook.com +noall +answer
      ;; global options: +cmd
      facebook.com.		474	IN	AAAA	2a03:2880:2110:df07:face:b00c:0:1
      • +2
        Хм. У меня не прошло, когда статью писал. Сейчас поправлю.
      • +4
        Очень не хватает какой-то базы знаний по вашем продуктам, какая например есть у широко известного в узких кругах.

        Пытался настроить v6 на облачном сервере — адрес из диапазона на интерфейс повесить смог, входящие пакеты полетели. А прописать маршрут наружу — нет, выскакивала какая-то непонятная ошибка. Быстрое гугление не помогло, забил. Здесь бы пригодился какой-то howto/faq.
        • 0
          Угу. На самом деле я нашёл некоторые ошибки в планировании ipv6, мы будем переделывать allocation (на этот раз окончательно). Закончим — будет всё просто.

        • +2
          Итого — в postgres полностью сломан механизм управления IPv6 сетями

          Я бы не сказал, что это в postgres что-то «сломано». Более правильно было бы сказать, что в Postgres не хватает функционала для решения Вашей конкретной задачи.
          • –1
            Ну, это вопрос трактовки. С моей точки зрения — некий код работает с ipv4, но не может (из-за БД) работать с ipv6. Это явно ошибка проектирования postgres, в которой не предусмотрели суммирование мелких (/64) сетей.
            • +5
              Это к вопросу что называть багом, а что не называть. :) Механизм управления IPv6 сетями может быть сломан, если было где-то заявлено что он работает. Т.е. написано, что должно работать одним образом, а работает совсем не так. Пока вроде никто и не обещал что к inet/cidr можно прибавить inet/сidr или хотябы numeric.

              BTW postgresql отлично работает с большими числами, надо лишь только попросить.
              select pow(2::numeric, 80::numeric) + 1208925819614629174706176::numeric;
              
              • 0
                А теперь добавьте это к inet. В доке сказано, можно bigint'ы прибавлять, а не numeric'и. Потому я и говорю, что это ошибка проектировки. Разрешили бы numeric'и добавлять, вопросов бы не было. Хотя я бы предпочёл inet+inet.
                • 0
                  ак напишите :)
                  PostgreSQL User-defined operators
                  • 0
                    Небольшой аппроксимацией этого подхода мы переходим к «патчи в апстрим»/«своя БД».
                    • +1
                      Не надо патчей в апстрим. Свои динамически подгружаемые функции для Postgresql пишутся на коленке. Нам было надо — мы писали.
                      Если кратко вам будет достаточно конвертировать inet и cidr в numeric там с ним работать и потом конвертировать обратно. Для этого достаточно будет взять bytea представление у inet/cidr его записать в строку и строку отдать конструктору numeric-a. Который и вернуть как результат. То же самое, но в обратном порядке для конверсии numeric в inet/cidr. Вложитесь в 100 строк кода.
                      • 0
                        можно проще :) дважды прибавить 2^40 :)
                        select '2a01:4f8:130:1065::2'::inet + pow(2, 40)::bigint + pow(2, 40)::bigint
                        • +1
                          2^40 + 2^40 = 2^41 :)
                          • 0
                            хорошо, вот еще особоизвращённый способ :) 200мс выполняется
                            WITH RECURSIVE t(inet, n) AS (
                                VALUES ('2a01:4f8:130:1065::2'::inet, 0)
                              UNION ALL
                                SELECT inet + pow(2,62)::bigint, n+1 FROM t WHERE n < pow(2,18)
                            )
                            SELECT inet, n FROM t order by n desc 
                            limit 1;
                            
                            • +1
                              Я думаю, надо начать с аксиом Пеано, после того, как будет определено рекурсивное сложение через +1, можно будет складывать любые цифры.

                              По сути — не интересно, мы просто математику вытащили в нормальный код.
                        • 0
                          Ок, спасибо за информацию.
                    • 0
                      Вот и я говорю, что всё работает по доке. А вы говорите — поломано. :)
                      • 0
                        Если документация не охватывает все случаи законного применения описанного, и в этих случаях наблюдается отклонение то ожидаемого — то это ошибка. Не кода, но проектирования.
                        • 0
                          в документации написано что вы к inet можете прибавить bigint. более там ничего не написано
                          • 0
                            В тот момент, когда в документацию и код проектировалось inet+bigint, в этот момент была совершена ошибка. Именно такие ошибки и называются «ошибки проектирования».
                  • 0
                    А зачем Вы вообще храните и считаете что-то меньше /64?
                    • 0
                      В ipv4 нормальным считается суммировать, например, + /29. В IPv6, да, меньше, чем /64 редко нужно.
                • 0
                  У меня на VPS есть ipv6 подсеть /64, и с адресов, с самого сервера хорошо пингуются v6 адреса. Я бы хотел прокинуть часть подсети в openvpn, чтобы можно было ходить по ipv6 адресам сквозь vpn. Как настроить в таком случае маршрутизацию?
                  Максимум, что у меня получилось сделать — через VPN пингуется сервер по ipv6, но не дальше.
                  В интернете очень мало материала для понимания таких вещей (или я плохо искал), что посоветуете почитать?
                  • +1
                    В ipv6 не должно быть сетей шире (больше) /64. Особенно сбивает тот факт, что на интерфейс вы можете повесить сеть любого размера. Однако при попытке ее использования, если она больше /64, вы ступаете на поле с плотно уложенными граблями.
                    Вот тут хорошо описано.
                    • 0
                      По ссылке глупость. RIPE NCC на своих тренингах по ipv6 allocation говорит, что размер allocation должен определяться задачей. Если предусматривается наличие дальнейшего деления — то сеть должна быть большего размера.

                      На упражнениях в зависимости от задачи на одного клиента LIR'ам предлагалось выделять от /56-/48 до /32.

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

                      Например, если у нас по numbering plan полагается /48 на проект, то должны ли мы в СУБД хранить пачку /64, или таки несколько /48?
                      • 0
                        Приношу извинения, думал, комментарий мне адресуется (и подумал, что «больше, чем /64 — это /48 и т.д.»).
                      • 0
                        С точки зрения маршрутизации ситуация ничем не отличается от ipv4. Для того, чтобы иметь возможность «утащить» маршрут в туннель вам нужно, чтобы сеть, которую вы утаскиваете, была бы не locally connected. Условно говоря, если схема вот такая:

                        router: ::1 — client ::/64

                        то клиент не может маршрутизировать меньшие кусочки сетей через туннель.
                        Правильная схема:
                        router: ----------------   client 1::2  ~~~~ tunnel 99::/64
                        1::1
                        99::/64 via 1::2
                        

                        На VDS вам врят ли дадут маршрутизируемую сеть, так что максимум, что вы можете сделать, это unnumbered tunnel или бридж.

                        Я бы в этой ситуации просто через туннель утащил кусок бриджа и юзал нужный поддиапазон (не подсеть!) на удалённом узле.

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

                      Самое читаемое