[О блоге] [наверх] [пред] [2018-08-22 22:17:32+03:00] [7aa88f79890fe720093162a03d0f78fcdefb0b99]
Темы: [ipv6][tip]

IPv6 link-local адреса в /etc/hosts

Сегодня коллега хотел было прописать на своём Arch GNU/Linux IPv6
link-local адрес в /etc/hosts. Не получилось. Поискал информацию и
говорит что туда нельзя link-local, а только routable адреса.

В FreeBSD оно работает без проблем. Казалось бы -- какая разница какого
типа адрес? Но... Linux мир умудряется даже тут костылять. В FreeBSD уже
с 9.0 версии есть возможность собрать ядро полностью без IPv4 поддержки
-- IPv6 only. В GNU/Linux, быстрый поиск, показывает что вроде как
нельзя такое сделать.

Ещё я помню случай навсегда изменивший моё почтение к Linux-у (именно
ядру): loopback блочное устройство на самом деле не такое уж и
полноценное. Например мы можете сделать losetup, создав loopback block
device, но например partition table с него не подгрузится -- для этого
нужно использовать отдельный костыль в виде kpartx. В FreeBSD (тогда это
была 5.x версия) -- без разницы loopback оно или нет: оно полноценно
воспринимается системой, без каких-либо костылей и дополнительных
телодвижений.

Лично для меня все эти факты (IPv6-only, link-local в hosts, kpartx)
показывают насколько нецелостен Linux и как много в нём подпорок которые
не убрать просто так. Это говорит, как мне (разработчику) кажется, о
плохом проектировании архитектуры всего что в ядре происходит.

    [оставить комментарий]
    комментарий 0:
    From: kmeaw
    Date: 2020-10-12 14:35:48Z
    
    > Поискал информацию и
    > говорит что туда нельзя link-local, а только routable адреса.
    
    Зависит от реализации getaddrinfo. В musl такой проблемы нет.
    В glibc, действительно, getaddrinfo и inet_pton не понимают zone
    identifier в IPv6-адресах.
    
    > В FreeBSD уже
    > с 9.0 версии есть возможность собрать ядро полностью без IPv4 поддержки
    > -- IPv6 only.
    
    Я уже задавал этот вопрос на Хабре, но повторюсь, так как не получил
    ответа — а как оно работает в FreeBSD? Я сходу не могу придумать, как
    красиво отвязать реализацию TCP от IP (проблемы должны возникнуть,
    например, при вычислении pseudoheader checksum).
    
    > loopback блочное устройство на самом деле не такое уж и
    > полноценное. Например мы можете сделать losetup, создав loopback block
    > device, но например partition table с него не подгрузится
    
    Для поддержания partition table нужно резервировать device minor
    numbers. Поскольку пользователи гораздо чаще создают много loopback
    devices, чем разделов на одном loopback device, по-умолчанию размер
    выделяемого диапозона равен единице. Это несложно изменить, для этого
    есть параметр max_part у модуля loop:
    
       modprobe loop max_part=8
    
    После чего вместь с /dev/loopX появятся устройства /dev/loopXpY.
    
    --
    kmeaw
    
    комментарий 1:
    From: kmeaw
    Date: 2020-10-12 15:01:10Z
    
    Посмотрел в исходники glibc, и понял, что всё на самом деле не совсем
    так.
    
    Современные версии getaddrinfo вполне умеют scope identifier, и кладут
    его в правильное место — в sockaddr_in6.sin6_scope_id.
    
    А inet_pton(af=AF_INET6), которым пользуется парсер /etc/hosts,
    возвращает результат в виде struct in6_addr. По-другому эта функция не
    может работать (например, писать в sockaddr_in6 или записывать scope
    identifier куда-то ещё), потому что это часть POSIX.
    
    Проблема в glibc заключается в том, что парсер hosts использует
    inet_pton, а не некую внутреннюю часть getaddrinfo. Полноценный
    getaddrinfo он по понятным причинам использовать не может.
    
    В musl же для этого используется внутренняя функция __lookup_ipliteral,
    заполняющая уже внутреннюю struct address.
    
    Я пока не могу придумать хороший способ написать программу, которая бы,
    ограничиваясь только тем, что есть в POSIX, парсила бы IPv6 адрес в
    struct sockaddr_in6 без работающего getaddrinfo (с работающим легко,
    hints.ai_flags=AI_NUMERICHOST). Возможно, разработчики glibc считают,
    что плохо в реализации libnss/files-hosts использовать функцию
    getaddrinfo, которая, в свою очередь, сама использует libnss, порождая
    циклическую зависимость, ведь чтобы её разорвать, придётся знать детали
    реализации динамически загружаемой библиотеки (неизвестно какой, ведь
    через nsswitch.conf пользователь может указать любую).
    
    musl, в силу простоты своего дизайна, не пытается быть такой же
    расширяемой, поэтому механизма, похожего на nss, там просто нет. Поэтому
    и проблемы этой нет — функции могут использовать части друг-друга и
    завязываться на детали реализации.
    
    комментарий 2:
    From: Sergey Matveev
    Date: 2020-10-12 15:06:29Z
    
    *** kmeaw [2020-10-12 17:31]:
    >Зависит от реализации getaddrinfo. В musl такой проблемы нет.
    
    Учитывая что большинство GNU/Linux имеют glibc, это всё равно касается
    большинства. Но здорово, буду знать про musl. У меня в голове GNU софт
    выглядит как монструозный и огромный (то что я видел со своими
    познаниями C -- это подтверждают), но при этом и самый фичастый и
    навороченный. А тут вот обратное.
    
    >Я уже задавал этот вопрос на Хабре, но повторюсь
    
    Если речь про комментарии к статье про "FreeBSD гораздо лучше...", то я
    там их довольно быстро уже перестал читать, ибо статья holywar-ная, и
    поэтому там сплошные holywar-ы :-)
    
    >а как оно работает в FreeBSD? Я сходу не могу придумать, как
    >красиво отвязать реализацию TCP от IP (проблемы должны возникнуть,
    >например, при вычислении pseudoheader checksum).
    
    Как конкретно -- не отвечу, не смотрел глубоко. Когда делал IPsec ESP
    стэк на Go, то много лазал по их коду и отмечал что нигде гвоздями
    прибитых IPv4 не видел. Чисто технически/программерски: нет же проблем
    сделать IPv4/IPv6-only код. Главное помнить что IP есть, как минимум,
    двух версий и не делать hardcode.
    https://wiki.freebsd.org/IPv6/IPv6OnlySnapshots
    
    А в чём проблемы будут с псевдозаголовком в TCP? Он зависит от заголовка
    IP, но ведь TCP/UDP/ICMP (про остальные не знаю) достаточно знать не
    само значение псевдозаголовка (что может быть просто интерфейсным
    вызовом сетевого уровня), а его уже посчитанное значение. Я возможно
    рассматриваю только простейшие случаи, где всё гладко, но вот мне
    пришлось при реализации IPsec ESP пересчитывать контрольные суммы для
    TCP/UDP/ICMP пакетов и у меня такой код:
    
        // Recompute checksums
        if gotIPv6 {
            csum = ipv6PseudoHdrCsum(src, dst)
        } else {
            csum = ipv4PseudoHdrCsum(src, dst)
        }
        switch nextHdr {
        case gostipsec.ProtoTCP:
            body[16] = 0
            body[17] = 0
            binary.BigEndian.PutUint16(
                body[16:18],
                tcpipCsum(csum, gostipsec.ProtoTCP, body),
            )
        case gostipsec.ProtoUDP:
            body[6] = 0
            body[7] = 0
            binary.BigEndian.PutUint16(
                body[6:8],
                tcpipCsum(csum, gostipsec.ProtoUDP, body),
            )
        [...]
        case gostipsec.ProtoICMPv6:
        case gostipsec.ProtoICMP:
    
    Где вообще у пакета просто мог бы быть .HdrCsum() интерфейс, который
    выдал бы мне csum уже дальше используемый в конкретных транспортных
    протоколах.
    
    >Это несложно изменить, для этого есть параметр max_part у модуля loop:
    
    Мне, как простому пользователю, это всё выглядит магией :-). Казалось
    бы (для меня), простейшие действия, но требуют постоянно каких-то
    донастроек, опций и конфигурирования. Я просто хочу получить loopback
    блочное устройство! Как и кучу других вещей. И вот FreeBSD это даёт
    именно как мне надо. Безусловно, всё то же самое касается и любой другой
    ОС -- у всех свои за и против, но GNU/Linux точно не моё :-)
    
    комментарий 3:
    From: Sergey Matveev
    Date: 2020-10-12 15:11:39Z
    
    *** kmeaw [2020-10-12 17:57]:
    >Возможно, разработчики glibc считают,
    >что плохо в реализации libnss/files-hosts использовать функцию
    >getaddrinfo, которая, в свою очередь, сама использует libnss, порождая
    >циклическую зависимость
    
    Это первое что пришло мне в голову на вопрос "почему glibc использует
    другую функцию". Логично и можно их понять.
    
    >musl, в силу простоты своего дизайна, не пытается быть такой же
    >расширяемой, поэтому механизма, похожего на nss, там просто нет.
    
    И это тоже могу понять и уважать такой подход! В
    aaf073b9d4b9bed91492e0af3ba9c09144849bb1 как раз тоже был показан подход
    к isalnum() функции в musl и glibc. И у обоих есть же свои за и против!
    
    Но, опять же, мне, как пользователю ОС, хотелось бы чтобы я мог
    прописать link-local-ы в /etc/hosts, как это позволяет FreeBSD. И я хочу
    так не потому что FreeBSD так умеет, а потому что я реально это хочу
    сделать для своего же удобства :-)