Компания
256,37
рейтинг
24 июня 2013 в 15:33

Разное → Практика 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), то следует аккуратно различать, чьим анонсам доверяем, а чьим — нет. Довериться своим собственным анонсам — это нелепо и не работает.
Автор: @amarao
Селектел
рейтинг 256,37

Комментарии (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 или бридж.

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

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

Самое читаемое Разное