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

Что: 7aa88f79890fe720093162a03d0f78fcdefb0b99

Когда: 2018-08-22 22:17:32+03:00

Темы: 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


>Зависит от реализации 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


>Возможно, разработчики glibc считают,
>что плохо в реализации libnss/files-hosts использовать функцию
>getaddrinfo, которая, в свою очередь, сама использует libnss, порождая
>циклическую зависимость

Это первое что пришло мне в голову на вопрос "почему glibc использует
другую функцию". Логично и можно их понять.

>musl, в силу простоты своего дизайна, не пытается быть такой же
>расширяемой, поэтому механизма, похожего на nss, там просто нет.

И это тоже могу понять и уважать такой подход! В
aaf073b9d4b9bed91492e0af3ba9c09144849bb1 как раз тоже был показан подход
к isalnum() функции в musl и glibc. И у обоих есть же свои за и против!

Но, опять же, мне, как пользователю ОС, хотелось бы чтобы я мог
прописать link-local-ы в /etc/hosts, как это позволяет FreeBSD. И я хочу
так не потому что FreeBSD так умеет, а потому что я реально это хочу
сделать для своего же удобства :-)

Сгенерирован: SGBlog 0.34.0